| # kio Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Source Files

# 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 <sys/stat.h>
    00038 #include <unistd.h>
    00039 //#include <time.h>
    00040 #include <fcntl.h>
    00041 #include <signal.h>
    00042 #include <errno.h>
    00043 #endif
    00044
    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->dn_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 #ifdef HAVE_FAM
    00185   // It's possible that FAM server can't be started
    00186   if (FAMOpen(&fc) ==0) {
    00187     available += ", FAM";
    00188     use_fam=true;
    00189     sn = new QSocketNotifier( FAMCONNECTION_GETFD(&fc),
    00190                               QSocketNotifier::Read, this);
    00191     connect( sn, SIGNAL(activated(int)),
    00192              this, SLOT(famEventReceived()) );
    00193   }
    00194   else {
    00195     kdDebug(7001) << "Can't use FAM (fam daemon not running?)" << endl;
    00196     use_fam=false;
    00197   }
    00198 #endif
    00199
    00200 #ifdef HAVE_DNOTIFY
    00201   supports_dnotify = true; // not guilty until proven guilty
    00202   rescan_all = false;
    00203   struct utsname uts;
    00204   int major, minor, patch;
    00205   if (uname(&uts) < 0)
    00206     supports_dnotify = false; // *shrug*
    00207   else if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) != 3)
    00208     supports_dnotify = false; // *shrug*
    00209   else if( major * 1000000 + minor * 1000 + patch < 2004019 ) { // <2.4.19
    00210     kdDebug(7001) << "Can't use DNotify, Linux kernel too old" << endl;
    00211     supports_dnotify = false;
    00212   }
    00213
    00214   if( supports_dnotify ) {
    00215     available += ", DNotify";
    00216
    00217     pipe(mPipe);
    00218     fcntl(mPipe[0], F_SETFD, FD_CLOEXEC);
    00219     fcntl(mPipe[1], F_SETFD, FD_CLOEXEC);
    00220     mSn = new QSocketNotifier( mPipe[0], QSocketNotifier::Read, this);
    00221     connect(mSn, SIGNAL(activated(int)), this, SLOT(slotActivated()));
    00222     connect(&mTimer, SIGNAL(timeout()), this, SLOT(slotRescan()));
    00223     // Install the signal handler only once
    00224     if ( dnotify_signal == 0 )
    00225     {
    00226        dnotify_signal = SIGRTMIN + 8;
    00227
    00228        struct sigaction act;
    00229        act.sa_sigaction = KDirWatchPrivate::dnotify_handler;
    00230        sigemptyset(&act.sa_mask);
    00231        act.sa_flags = SA_SIGINFO;
    00232 #ifdef SA_RESTART
    00233        act.sa_flags |= SA_RESTART;
    00234 #endif
    00235        sigaction(dnotify_signal, &act, NULL);
    00236
    00237        act.sa_sigaction = KDirWatchPrivate::dnotify_sigio_handler;
    00238        sigaction(SIGIO, &act, &old_sigio_act);
    00239     }
    00240   }
    00241   else
    00242   {
    00243     mPipe[0] = -1;
    00244     mPipe[1] = -1;
    00245   }
    00246 #endif
    00247
    00248   kdDebug(7001) << "Available methods: " << available << endl;
    00249 }
    00250
    00251 /* This should never be called, but doesn't harm */
    00252 KDirWatchPrivate::~KDirWatchPrivate()
    00253 {
    00254   timer->stop();
    00255
    00256   /* remove all entries being watched */
    00257   removeEntries(0);
    00258
    00259 #ifdef HAVE_FAM
    00260   if (use_fam) {
    00261     FAMClose(&fc);
    00262     kdDebug(7001) << "KDirWatch deleted (FAM closed)" << endl;
    00263   }
    00264 #endif
    00265 #ifdef HAVE_DNOTIFY
    00266   close(mPipe[0]);
    00267   close(mPipe[1]);
    00268 #endif
    00269 }
    00270
    00271 #ifdef HAVE_DNOTIFY
    00272 void KDirWatchPrivate::slotActivated()
    00273 {
    00274    char dummy_buf[100];
    00275    read(mPipe[0], &dummy_buf, 100);
    00276
    00277    if (!mTimer.isActive())
    00278       mTimer.start(200, true);
    00279 }
    00280
    00281 /* In DNOTIFY mode, only entries which are marked dirty are scanned.
    00282  * We first need to mark all yet nonexistant, but possible created
    00283  * entries as dirty...
    00284  */
    00285 void KDirWatchPrivate::Entry::propagate_dirty()
    00286 {
    00287   Entry* sub_entry;
    00288   for(sub_entry = m_entries.first(); sub_entry; sub_entry = m_entries.next())
    00289   {
    00290      if (!sub_entry->dn_dirty)
    00291      {
    00292         sub_entry->dn_dirty = true;
    00293         sub_entry->propagate_dirty();
    00294      }
    00295   }
    00296 }
    00297
    00298 #else // !HAVE_DNOTIFY
    00299 // slots always have to be defined...
    00300 void KDirWatchPrivate::slotActivated() {}
    00301 #endif
    00302
    00303 /* A KDirWatch instance is interested in getting events for
    00304  * this file/Dir entry.
    00305  */
    00306 void KDirWatchPrivate::Entry::addClient(KDirWatch* instance)
    00307 {
    00308   Client* client = m_clients.first();
    00309   for(;client; client = m_clients.next())
    00310     if (client->instance == instance) break;
    00311
    00312   if (client) {
    00313     client->count++;
    00314     return;
    00315   }
    00316
    00317   client = new Client;
    00318   client->instance = instance;
    00319   client->count = 1;
    00320   client->watchingStopped = instance->isStopped();
    00321   client->pending = NoChange;
    00322
    00323   m_clients.append(client);
    00324 }
    00325
    00326 void KDirWatchPrivate::Entry::removeClient(KDirWatch* instance)
    00327 {
    00328   Client* client = m_clients.first();
    00329   for(;client; client = m_clients.next())
    00330     if (client->instance == instance) break;
    00331
    00332   if (client) {
    00333     client->count--;
    00334     if (client->count == 0) {
    00335       m_clients.removeRef(client);
    00336       delete client;
    00337     }
    00338   }
    00339 }
    00340
    00341 /* get number of clients */
    00342 int KDirWatchPrivate::Entry::clients()
    00343 {
    00344   int clients = 0;
    00345   Client* client = m_clients.first();
    00346   for(;client; client = m_clients.next())
    00347     clients += client->count;
    00348
    00349   return clients;
    00350 }
    00351
    00352
    00353 KDirWatchPrivate::Entry* KDirWatchPrivate::entry(const QString& _path)
    00354 {
    00355 // we only support absolute paths
    00356   if (_path.left(1) != "/") {
    00357     return 0;
    00358   }
    00359
    00360   QString path = _path;
    00361
    00362   if ( path.length() > 1 && path.right(1) == "/" )
    00363     path.truncate( path.length() - 1 );
    00364
    00365   EntryMap::Iterator it = m_mapEntries.find( path );
    00366   if ( it == m_mapEntries.end() )
    00367     return 0;
    00368   else
    00369     return &(*it);
    00370 }
    00371
    00372 // set polling frequency for a entry and adjust global freq if needed
    00373 void KDirWatchPrivate::useFreq(Entry* e, int newFreq)
    00374 {
    00375   e->freq = newFreq;
    00376
    00377   // a reasonable frequency for the global polling timer
    00378   if (e->freq < freq) {
    00379     freq = e->freq;
    00380     if (timer->isActive()) timer->changeInterval(freq);
    00381     kdDebug(7001) << "Global Poll Freq is now " << freq << " msec" << endl;
    00382   }
    00383 }
    00384
    00385
    00386 #if defined(HAVE_FAM)
    00387 // setup FAM notification, returns false if not possible
    00388 bool KDirWatchPrivate::useFAM(Entry* e)
    00389 {
    00390   if (!use_fam) return false;
    00391
    00392   e->m_mode = FAMMode;
    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->dn_dirty = false;
    00452     if (e->m_status == Normal) {
    00453       int fd = open(QFile::encodeName(e->path).data(), O_RDONLY);
    00454       if (fd<0) {
    00455         e->m_mode = UnknownMode;
    00456         return false;
    00457       }
    00458
    00459       int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
    00460       // if dependant is a file watch, we check for MODIFY & ATTRIB too
    00461       for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
    00462         if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB; break; }
    00463
    00464       if(fcntl(fd, F_SETSIG, dnotify_signal) < 0 ||
    00465          fcntl(fd, F_NOTIFY, mask) < 0) {
    00466
    00467         kdDebug(7001) << "Not using Linux Directory Notifications."
    00468                       << endl;
    00469         supports_dnotify = false;
    00470         ::close(fd);
    00471         e->m_mode = UnknownMode;
    00472         return false;
    00473       }
    00474
    00475       fd_Entry.replace(fd, e);
    00476       e->dn_fd = fd;
    00477
    00478       kdDebug(7001) << " Setup DNotify (fd " << fd
    00479                     << ") for " << e->path << endl;
    00480     }
    00481     else { // NotExisting
    00482       addEntry(0, QDir::cleanDirPath(e->path+"/.."), e, true);
    00483     }
    00484   }
    00485   else { // File
    00486     // we always watch the directory (DNOTIFY can't watch files alone)
    00487     // this notifies us about changes of files therein
    00488     addEntry(0, QFileInfo(e->path).dirPath(true), e, true);
    00489   }
    00490
    00491   return true;
    00492 }
    00493 #endif
    00494
    00495
    00496 bool KDirWatchPrivate::useStat(Entry* e)
    00497 {
    00498   if (KIO::probably_slow_mounted(e->path))
    00499     useFreq(e, m_nfsPollInterval);
    00500   else
    00501     useFreq(e, m_PollInterval);
    00502
    00503   if (e->m_mode != StatMode) {
    00504     e->m_mode = StatMode;
    00505     statEntries++;
    00506
    00507     if ( statEntries == 1 ) {
    00508       // if this was first STAT entry (=timer was stopped)
    00509       timer->start(freq);      // then start the timer
    00510       kdDebug(7001) << " Started Polling Timer, freq " << freq << endl;
    00511     }
    00512   }
    00513
    00514   kdDebug(7001) << " Setup Stat (freq " << e->freq
    00515                 << ") for " << e->path << endl;
    00516
    00517   return true;
    00518 }
    00519
    00520
    00521 /* If <instance> !=0, this KDirWatch instance wants to watch at <_path>,
    00522  * providing in <isDir> the type of the entry to be watched.
    00523  * Sometimes, entries are dependant on each other: if <sub_entry> !=0,
    00524  * this entry needs another entry to watch himself (when notExistent).
    00525  */
    00526 void KDirWatchPrivate::addEntry(KDirWatch* instance, const QString& _path,
    00527                                 Entry* sub_entry, bool isDir)
    00528 {
    00529   QString path = _path;
    00530   if (path.startsWith("/dev/") || (path == "/dev"))
    00531     return; // Don't even go there.
    00532
    00533   if ( path.length() > 1 && path.right(1) == "/" )
    00534     path.truncate( path.length() - 1 );
    00535
    00536   EntryMap::Iterator it = m_mapEntries.find( path );
    00537   if ( it != m_mapEntries.end() )
    00538   {
    00539     if (sub_entry) {
    00540        (*it).m_entries.append(sub_entry);
    00541        kdDebug(7001) << "Added already watched Entry " << path
    00542                      << " (for " << sub_entry->path << ")" << endl;
    00543 #ifdef HAVE_DNOTIFY
    00544        Entry* e = &(*it);
    00545        if( e->dn_fd > 0 ) {
    00546          int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
    00547          // if dependant is a file watch, we check for MODIFY & ATTRIB too
    00548          for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
    00549            if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB; break; }
    00550          if( fcntl(e->dn_fd, F_NOTIFY, mask) < 0) { // shouldn't happen
    00551            ::close(e->dn_fd);
    00552            e->m_mode = UnknownMode;
    00553            fd_Entry.remove(e->dn_fd);
    00554            e->dn_fd = 0;
    00555            useStat( e );
    00556          }
    00557        }
    00558 #endif
    00559     }
    00560     else {
    00561        (*it).addClient(instance);
    00562        kdDebug(7001) << "Added already watched Entry " << path
    00563                      << " (now " <<  (*it).clients() << " clients)"
    00564                      << QString(" [%1]").arg(instance->name()) << endl;
    00565     }
    00566     return;
    00567   }
    00568
    00569   // we have a new path to watch
    00570
    00571   QFileInfo info(path);
    00572
    00573   Entry newEntry;
    00574   m_mapEntries.insert( path, newEntry );
    00575   // the insert does a copy, so we have to use <e> now
    00576   Entry* e = &(m_mapEntries[path]);
    00577
    00578   if (info.exists()) {
    00579     e->isDir = info.isDir();
    00580
    00581     if (e->isDir && !isDir)
    00582       qWarning("KDirWatch: %s is a directory. Use addDir!", path.ascii());
    00583     else if (!e->isDir && isDir)
    00584       qWarning("KDirWatch: %s is a file. Use addFile!", path.ascii());
    00585
    00586     e->m_ctime = info.lastModified();
    00587     e->m_status = Normal;
    00588   }
    00589   else {
    00590     e->isDir = isDir;
    00591     e->m_ctime = QDateTime(); // invalid
    00592     e->m_status = NonExistent;
    00593   }
    00594
    00595   e->path = path;
    00596   if (sub_entry)
    00597      e->m_entries.append(sub_entry);
    00598   else
    00599     e->addClient(instance);
    00600
    00601   kdDebug(7001) << "Added " << (e->isDir ? "Dir ":"File ") << path
    00602                 << (e->m_status == NonExistent ? " NotExisting" : "")
    00603                 << (sub_entry ? QString(" for %1").arg(sub_entry->path) : QString(""))
    00604                 << (instance ? QString(" [%1]").arg(instance->name()) : QString(""))
    00605                 << endl;
    00606
    00607
    00608   // now setup the notification method
    00609   e->m_mode = UnknownMode;
    00610   e->msecLeft = 0;
    00611
    00612 #if defined(HAVE_FAM)
    00613   if (useFAM(e)) return;
    00614 #endif
    00615
    00616 #ifdef HAVE_DNOTIFY
    00617   if (useDNotify(e)) return;
    00618 #endif
    00619
    00620   useStat(e);
    00621 }
    00622
    00623
    00624 void KDirWatchPrivate::removeEntry( KDirWatch* instance,
    00625                                     const QString& _path, Entry* sub_entry )
    00626 {
    00627   Entry* e = entry(_path);
    00628   if (!e) {
    00629     kdWarning(7001) << "KDirWatch::removeDir can't handle '" << _path << "'" << endl;
    00630     return;
    00631   }
    00632
    00633   if (sub_entry)
    00634     e->m_entries.removeRef(sub_entry);
    00635   else
    00636     e->removeClient(instance);
    00637
    00638   if (e->m_clients.count() || e->m_entries.count())
    00639     return;
    00640
    00641   if (delayRemove) {
    00642     // removeList is allowed to contain any entry at most once
    00643     if (removeList.findRef(e)==-1)
    00644     removeList.append(e);
    00645     // now e->isValid() is false
    00646     return;
    00647   }
    00648
    00649 #ifdef HAVE_FAM
    00650   if (e->m_mode == FAMMode) {
    00651     if ( e->m_status == Normal) {
    00652       FAMCancelMonitor(&fc, &(e->fr) );
    00653       kdDebug(7001) << "Cancelled FAM (Req "
    00654                     << FAMREQUEST_GETREQNUM(&(e->fr))
    00655                     << ") for " << e->path << endl;
    00656     }
    00657     else {
    00658       if (e->isDir)
    00659         removeEntry(0, QDir::cleanDirPath(e->path+"/.."), e);
    00660       else
    00661         removeEntry(0, QFileInfo(e->path).dirPath(true), e);
    00662     }
    00663   }
    00664 #endif
    00665
    00666 #ifdef HAVE_DNOTIFY
    00667   if (e->m_mode == DNotifyMode) {
    00668     if (!e->isDir) {
    00669       removeEntry(0, QFileInfo(e->path).dirPath(true), e);
    00670     }
    00671     else { // isDir
    00672       // must close the FD.
    00673       if ( e->m_status == Normal) {
    00674         if (e->dn_fd) {
    00675           ::close(e->dn_fd);
    00676           fd_Entry.remove(e->dn_fd);
    00677
    00678           kdDebug(7001) << "Cancelled DNotify (fd " << e->dn_fd
    00679                         << ") for " << e->path << endl;
    00680           e->dn_fd = 0;
    00681
    00682         }
    00683       }
    00684       else {
    00685         removeEntry(0, QDir::cleanDirPath(e->path+"/.."), e);
    00686       }
    00687     }
    00688   }
    00689 #endif
    00690
    00691   if (e->m_mode == StatMode) {
    00692     statEntries--;
    00693     if ( statEntries == 0 ) {
    00694       timer->stop(); // stop timer if lists are empty
    00695       kdDebug(7001) << " Stopped Polling Timer" << endl;
    00696     }
    00697   }
    00698
    00699   kdDebug(7001) << "Removed " << (e->isDir ? "Dir ":"File ") << e->path
    00700                 << (sub_entry ? QString(" for %1").arg(sub_entry->path) : QString(""))
    00701                 << (instance ? QString(" [%1]").arg(instance->name()) : QString(""))
    00702                 << endl;
    00703   m_mapEntries.remove( e->path ); // <e> not valid any more
    00704 }
    00705
    00706
    00707 /* Called from KDirWatch destructor:
    00708  * remove <instance> as client from all entries
    00709  */
    00710 void KDirWatchPrivate::removeEntries( KDirWatch* instance )
    00711 {
    00712   QPtrList<Entry> list;
    00713   int minfreq = 3600000;
    00714
    00715   // put all entries where instance is a client in list
    00716   EntryMap::Iterator it = m_mapEntries.begin();
    00717   for( ; it != m_mapEntries.end(); ++it ) {
    00718     Client* c = (*it).m_clients.first();
    00719     for(;c;c=(*it).m_clients.next())
    00720       if (c->instance == instance) break;
    00721     if (c) {
    00722       c->count = 1; // forces deletion of instance as client
    00723       list.append(&(*it));
    00724     }
    00725     else if ( (*it).m_mode == StatMode && (*it).freq < minfreq )
    00726       minfreq = (*it).freq;
    00727   }
    00728
    00729   for(Entry* e=list.first();e;e=list.next())
    00730     removeEntry(instance, e->path, 0);
    00731
    00732   if (minfreq > freq) {
    00733     // we can decrease the global polling frequency
    00734     freq = minfreq;
    00735     if (timer->isActive()) timer->changeInterval(freq);
    00736     kdDebug(7001) << "Poll Freq now " << freq << " msec" << endl;
    00737   }
    00738 }
    00739
    00740 // instance ==0: stop scanning for all instances
    00741 bool KDirWatchPrivate::stopEntryScan( KDirWatch* instance, Entry* e)
    00742 {
    00743   int stillWatching = 0;
    00744   Client* c = e->m_clients.first();
    00745   for(;c;c=e->m_clients.next()) {
    00746     if (!instance || instance == c->instance)
    00747       c->watchingStopped = true;
    00748     else if (!c->watchingStopped)
    00749       stillWatching += c->count;
    00750   }
    00751
    00752   kdDebug(7001) << instance->name() << " stopped scanning " << e->path
    00753                 << " (now " << stillWatching << " watchers)" << endl;
    00754
    00755   if (stillWatching == 0) {
    00756     // if nobody is interested, we don't watch
    00757     e->m_ctime = QDateTime(); // invalid
    00758     //    e->m_status = Normal;
    00759   }
    00760   return true;
    00761 }
    00762
    00763 // instance ==0: start scanning for all instances
    00764 bool KDirWatchPrivate::restartEntryScan( KDirWatch* instance, Entry* e,
    00765                                          bool notify)
    00766 {
    00767   int wasWatching = 0, newWatching = 0;
    00768   Client* c = e->m_clients.first();
    00769   for(;c;c=e->m_clients.next()) {
    00770     if (!c->watchingStopped)
    00771       wasWatching += c->count;
    00772     else if (!instance || instance == c->instance) {
    00773       c->watchingStopped = false;
    00774       newWatching += c->count;
    00775     }
    00776   }
    00777   if (newWatching == 0)
    00778     return false;
    00779
    00780   kdDebug(7001) << instance->name() << " restarted scanning " << e->path
    00781                 << " (now " << wasWatching+newWatching << " watchers)" << endl;
    00782
    00783   // restart watching and emit pending events
    00784
    00785   int ev = NoChange;
    00786   if (wasWatching == 0) {
    00787     if (!notify) {
    00788       QFileInfo info(e->path);
    00789       if (info.exists()) {
    00790         e->m_ctime = info.lastModified();
    00791         e->m_status = Normal;
    00792       }
    00793       else {
    00794         e->m_ctime = QDateTime(); // invalid
    00795         e->m_status = NonExistent;
    00796       }
    00797     }
    00798     e->msecLeft = 0;
    00799     ev = scanEntry(e);
    00800   }
    00801   emitEvent(e,ev);
    00802
    00803   return true;
    00804 }
    00805
    00806 // instance ==0: stop scanning for all instances
    00807 void KDirWatchPrivate::stopScan(KDirWatch* instance)
    00808 {
    00809   EntryMap::Iterator it = m_mapEntries.begin();
    00810   for( ; it != m_mapEntries.end(); ++it )
    00811     stopEntryScan(instance, &(*it));
    00812 }
    00813
    00814
    00815 void KDirWatchPrivate::startScan(KDirWatch* instance,
    00816                                  bool notify, bool skippedToo )
    00817 {
    00818   if (!notify)
    00819     resetList(instance,skippedToo);
    00820
    00821   EntryMap::Iterator it = m_mapEntries.begin();
    00822   for( ; it != m_mapEntries.end(); ++it )
    00823     restartEntryScan(instance, &(*it), notify);
    00824
    00825   // timer should still be running when in polling mode
    00826 }
    00827
    00828
    00829 // clear all pending events, also from stopped
    00830 void KDirWatchPrivate::resetList( KDirWatch* /*instance*/,
    00831                                   bool skippedToo )
    00832 {
    00833   EntryMap::Iterator it = m_mapEntries.begin();
    00834   for( ; it != m_mapEntries.end(); ++it ) {
    00835
    00836     Client* c = (*it).m_clients.first();
    00837     for(;c;c=(*it).m_clients.next())
    00838       if (!c->watchingStopped || skippedToo)
    00839         c->pending = NoChange;
    00840   }
    00841 }
    00842
    00843 // Return event happened on <e>
    00844 //
    00845 int KDirWatchPrivate::scanEntry(Entry* e)
    00846 {
    00847 #ifdef HAVE_FAM
    00848   // we do not stat entries using FAM
    00849   if (e->m_mode == FAMMode) return NoChange;
    00850 #endif
    00851
    00852   // Shouldn't happen: Ignore "unknown" notification method
    00853   if (e->m_mode == UnknownMode) return NoChange;
    00854
    00855 #ifdef HAVE_DNOTIFY
    00856   if (e->m_mode == DNotifyMode) {
    00857     // we know nothing has changed, no need to stat
    00858     if(!e->dn_dirty) return NoChange;
    00859     e->dn_dirty = false;
    00860   }
    00861 #endif
    00862
    00863   if (e->m_mode == StatMode) {
    00864     // only scan if timeout on entry timer happens;
    00865     // e.g. when using 500msec global timer, a entry
    00866     // with freq=5000 is only watched every 10th time
    00867
    00868     e->msecLeft -= freq;
    00869     if (e->msecLeft>0) return NoChange;
    00870     e->msecLeft += e->freq;
    00871   }
    00872
    00873   QFileInfo info(e->path);
    00874   if (info.exists()) {
    00875
    00876     if (e->m_status == NonExistent) {
    00877       e->m_ctime = info.lastModified();
    00878       e->m_status = Normal;
    00879       return Created;
    00880     }
    00881
    00882     if ( e->m_ctime.isValid() &&
    00883          (info.lastModified() != e->m_ctime) ) {
    00884       e->m_ctime = info.lastModified();
    00885       return Changed;
    00886     }
    00887
    00888     return NoChange;
    00889   }
    00890
    00891   // dir/file doesn't exist
    00892
    00893   if (!e->m_ctime.isValid())
    00894     return NoChange;
    00895
    00896   e->m_ctime = QDateTime(); // invalid
    00897   e->m_status = NonExistent;
    00898
    00899   return Deleted;
    00900 }
    00901
    00902 /* Notify all interested KDirWatch instances about a given event on an entry
    00903  * and stored pending events. When watching is stopped, the event is
    00904  * added to the pending events.
    00905  */
    00906 void KDirWatchPrivate::emitEvent(Entry* e, int event, const QString &fileName)
    00907 {
    00908   QString path = e->path;
    00909   if (!fileName.isEmpty()) {
    00910     if (fileName[0] == '/')
    00911       path = fileName;
    00912     else
    00913       path += "/" + fileName;
    00914   }
    00915
    00916   Client* c = e->m_clients.first();
    00917   for(;c;c=e->m_clients.next()) {
    00918     if (c->instance==0 || c->count==0) continue;
    00919
    00920     if (c->watchingStopped) {
    00921       // add event to pending...
    00922       if (event == Changed)
    00923         c->pending |= event;
    00924       else if (event == Created || event == Deleted)
    00925         c->pending = event;
    00926       continue;
    00927     }
    00928     // not stopped
    00929     if (event == NoChange || event == Changed)
    00930       event |= c->pending;
    00931     c->pending = NoChange;
    00932     if (event == NoChange) continue;
    00933
    00934     if (event & Deleted) {
    00935       c->instance->setDeleted(path);
    00936       // emit only Deleted event...
    00937       continue;
    00938     }
    00939
    00940     if (event & Created) {
    00941       c->instance->setCreated(path);
    00942       // possible emit Change event after creation
    00943     }
    00944
    00945     if (event & Changed)
    00946       c->instance->setDirty(path);
    00947   }
    00948 }
    00949
    00950 // Remove entries which were marked to be removed
    00951 void KDirWatchPrivate::slotRemoveDelayed()
    00952 {
    00953   Entry* e;
    00954   delayRemove = false;
    00955   for(e=removeList.first();e;e=removeList.next())
    00956     removeEntry(0, e->path, 0);
    00957   removeList.clear();
    00958 }
    00959
    00960 /* Scan all entries to be watched for changes. This is done regularly
    00961  * when polling and once after a DNOTIFY signal. This is NOT used by FAM.
    00962  */
    00963 void KDirWatchPrivate::slotRescan()
    00964 {
    00965   EntryMap::Iterator it;
    00966
    00967   // People can do very long things in the slot connected to dirty(),
    00968   // like showing a message box. We don't want to keep polling during
    00969   // that time, otherwise the value of 'delayRemove' will be reset.
    00970   bool timerRunning = timer->isActive();
    00971   if ( timerRunning )
    00972     timer->stop();
    00973
    00974   // We delay deletions of entries this way.
    00975   // removeDir(), when called in slotDirty(), can cause a crash otherwise
    00976   delayRemove = true;
    00977
    00978 #ifdef HAVE_DNOTIFY
    00979   QPtrList<Entry> dList, cList;
    00980
    00981   // for DNotify method,
    00982   if (rescan_all)
    00983   {
    00984     // mark all as dirty
    00985     it = m_mapEntries.begin();
    00986     for( ; it != m_mapEntries.end(); ++it )
    00987       (*it).dn_dirty = true;
    00988     rescan_all = false;
    00989   }
    00990   else
    00991   {
    00992     // progate dirty flag to dependant entries (e.g. file watches)
    00993     it = m_mapEntries.begin();
    00994     for( ; it != m_mapEntries.end(); ++it )
    00995       if ( ((*it).m_mode == DNotifyMode) && (*it).dn_dirty )
    00996         (*it).propagate_dirty();
    00997   }
    00998
    00999 #endif
    01000
    01001   it = m_mapEntries.begin();
    01002   for( ; it != m_mapEntries.end(); ++it ) {
    01003     // we don't check invalid entries (i.e. remove delayed)
    01004     if (!(*it).isValid()) continue;
    01005
    01006     int ev = scanEntry( &(*it) );
    01007
    01008 #ifdef HAVE_DNOTIFY
    01009     if ((*it).m_mode == DNotifyMode) {
    01010       if ((*it).isDir && (ev == Deleted)) {
    01011         dList.append( &(*it) );
    01012
    01013         // must close the FD.
    01014         if ((*it).dn_fd) {
    01015           ::close((*it).dn_fd);
    01016           fd_Entry.remove((*it).dn_fd);
    01017           (*it).dn_fd = 0;
    01018         }
    01019       }
    01020
    01021       else if ((*it).isDir && (ev == Created)) {
    01022         // For created, but yet without DNOTIFYing ...
    01023         if ( (*it).dn_fd == 0) {
    01024           cList.append( &(*it) );
    01025           if (! useDNotify( &(*it) )) {
    01026             // if DNotify setup fails...
    01027             useStat( &(*it) );
    01028           }
    01029         }
    01030       }
    01031     }
    01032 #endif
    01033
    01034     if ( ev != NoChange )
    01035       emitEvent( &(*it), ev);
    01036   }
    01037
    01038
    01039 #ifdef HAVE_DNOTIFY
    01040   // Scan parent of deleted directories for new creation
    01041   Entry* e;
    01042   for(e=dList.first();e;e=dList.next())
    01043     addEntry(0, QDir::cleanDirPath( e->path+"/.."), e, true);
    01044
    01045   // Remove watch of parent of new created directories
    01046   for(e=cList.first();e;e=cList.next())
    01047     removeEntry(0, QDir::cleanDirPath( e->path+"/.."), e);
    01048 #endif
    01049
    01050   if ( timerRunning )
    01051     timer->start(freq);
    01052
    01053   QTimer::singleShot(0, this, SLOT(slotRemoveDelayed()));
    01054 }
    01055
    01056 #ifdef HAVE_FAM
    01057 void KDirWatchPrivate::famEventReceived()
    01058 {
    01059   static FAMEvent fe;
    01060
    01061   delayRemove = true;
    01062
    01063   while(use_fam && FAMPending(&fc)) {
    01064     if (FAMNextEvent(&fc, &fe) == -1) {
    01065       kdWarning(7001) << "FAM connection problem, switching to polling."
    01066                       << endl;
    01067       use_fam = false;
    01068       delete sn; sn = 0;
    01069
    01070       // Replace all FAMMode entries with DNotify/Stat
    01071       EntryMap::Iterator it;
    01072       it = m_mapEntries.begin();
    01073       for( ; it != m_mapEntries.end(); ++it )
    01074         if ((*it).m_mode == FAMMode && (*it).m_clients.count()>0) {
    01075 #ifdef HAVE_DNOTIFY
    01076           if (useDNotify( &(*it) )) continue;
    01077 #endif
    01078           useStat( &(*it) );
    01079         }
    01080     }
    01081     else
    01082       checkFAMEvent(&fe);
    01083   }
    01084
    01085   QTimer::singleShot(0, this, SLOT(slotRemoveDelayed()));
    01086 }
    01087
    01088 void KDirWatchPrivate::checkFAMEvent(FAMEvent* fe)
    01089 {
    01090   // Don't be too verbose ;-)
    01091   if ((fe->code == FAMExists) ||
    01092       (fe->code == FAMEndExist) ||
    01093       (fe->code == FAMAcknowledge)) return;
    01094
    01095   // $HOME/.X.err grows with debug output, so don't notify change
    01096   if ( *(fe->filename) == '.') {
    01097     if (strncmp(fe->filename, ".X.err", 6) == 0) return;
    01098     if (strncmp(fe->filename, ".xsession-errors", 16) == 0) return;
    01099   }
    01100
    01101   Entry* e = 0;
    01102   EntryMap::Iterator it = m_mapEntries.begin();
    01103   for( ; it != m_mapEntries.end(); ++it )
    01104     if (FAMREQUEST_GETREQNUM(&( (*it).fr )) ==
    01105        FAMREQUEST_GETREQNUM(&(fe->fr)) ) {
    01106       e = &(*it);
    01107       break;
    01108     }
    01109
    01110   // Entry* e = static_cast<Entry*>(fe->userdata);
    01111
    01112   kdDebug(7001) << "Processing FAM event ("
    01113                 << ((fe->code == FAMChanged) ? "FAMChanged" :
    01114                     (fe->code == FAMDeleted) ? "FAMDeleted" :
    01115                     (fe->code == FAMStartExecuting) ? "FAMStartExecuting" :
    01116                     (fe->code == FAMStopExecuting) ? "FAMStopExecuting" :
    01117                     (fe->code == FAMCreated) ? "FAMCreated" :
    01118                     (fe->code == FAMMoved) ? "FAMMoved" :
    01119                     (fe->code == FAMAcknowledge) ? "FAMAcknowledge" :
    01120                     (fe->code == FAMExists) ? "FAMExists" :
    01121                     (fe->code == FAMEndExist) ? "FAMEndExist" : "Unknown Code")
    01122                 << ", " << fe->filename
    01123                 << ", Req " << FAMREQUEST_GETREQNUM(&(fe->fr))
    01124                 << ")" << endl;
    01125
    01126   if (!e) {
    01127     // this happens e.g. for FAMAcknowledge after deleting a dir...
    01128     //    kdDebug(7001) << "No entry for FAM event ?!" << endl;
    01129     return;
    01130   }
    01131
    01132   if (e->m_status == NonExistent) {
    01133     kdDebug(7001) << "FAM event for nonExistent entry " << e->path << endl;
    01134     return;
    01135   }
    01136
    01137   if (e->isDir)
    01138     switch (fe->code)
    01139     {
    01140       case FAMDeleted:
    01141        // file absolute: watched dir
    01142         if (fe->filename[0] == '/')
    01143         {
    01144           // a watched directory was deleted
    01145
    01146           e->m_status = NonExistent;
    01147           FAMCancelMonitor(&fc, &(e->fr) ); // needed ?
    01148           kdDebug(7001) << "Cancelled FAMReq "
    01149                         << FAMREQUEST_GETREQNUM(&(e->fr))
    01150                         << " for " << e->path << endl;
    01151           // Scan parent for a new creation
    01152           addEntry(0, QDir::cleanDirPath( e->path+"/.."), e, true);
    01153         }
    01154         emitEvent(e, Deleted, QFile::decodeName(fe->filename));
    01155         break;
    01156
    01157       case FAMCreated: {
    01158           // check for creation of a directory we have to watch
    01159           Entry *sub_entry = e->m_entries.first();
    01160           for(;sub_entry; sub_entry = e->m_entries.next())
    01161             if (sub_entry->path == e->path + "/" + fe->filename) break;
    01162           if (sub_entry && sub_entry->isDir) {
    01163             QString path = e->path;
    01164             removeEntry(0,e->path,sub_entry); // <e> can be invalid here!!
    01165             sub_entry->m_status = Normal;
    01166             if (!useFAM(sub_entry))
    01167               useStat(sub_entry);
    01168
    01169             emitEvent(sub_entry, Created);
    01170           }
    01171           else emitEvent(e, Created, QFile::decodeName(fe->filename));
    01172           break;
    01173         }
    01174
    01175       case FAMChanged:
    01176         emitEvent(e, Changed, QFile::decodeName(fe->filename));
    01177
    01178       default:
    01179         break;
    01180     }
    01181   else switch (fe->code)
    01182     {
    01183       case FAMCreated: emitEvent(e, Created);
    01184                        break;
    01185       case FAMDeleted: emitEvent(e, Deleted);
    01186                        break;
    01187       case FAMChanged: emitEvent(e, Changed);
    01188                        break;
    01189       default: break;
    01190     }
    01191 }
    01192 #else
    01193 void KDirWatchPrivate::famEventReceived() {}
    01194 #endif
    01195
    01196
    01197 void KDirWatchPrivate::statistics()
    01198 {
    01199   EntryMap::Iterator it;
    01200
    01201   kdDebug(7001) << "Entries watched:" << endl;
    01202   if (m_mapEntries.count()==0) {
    01203     kdDebug(7001) << "  None." << endl;
    01204   }
    01205   else {
    01206     it = m_mapEntries.begin();
    01207     for( ; it != m_mapEntries.end(); ++it ) {
    01208       Entry* e = &(*it);
    01209       kdDebug(7001) << "  " << e->path << " ("
    01210                     << ((e->m_status==Normal)?"":"Nonexistent ")
    01211                     << (e->isDir ? "Dir":"File") << ", using "
    01212                     << ((e->m_mode == FAMMode) ? "FAM" :
    01213                         (e->m_mode == DNotifyMode) ? "DNotify" :
    01214                         (e->m_mode == StatMode) ? "Stat" : "Unknown Method")
    01215                     << ")" << endl;
    01216
    01217       Client* c = e->m_clients.first();
    01218       for(;c; c = e->m_clients.next()) {
    01219         QString pending;
    01220         if (c->watchingStopped) {
    01221           if (c->pending & Deleted) pending += "deleted ";
    01222           if (c->pending & Created) pending += "created ";
    01223           if (c->pending & Changed) pending += "changed ";
    01224           if (!pending.isEmpty()) pending = " (pending: " + pending + ")";
    01225           pending = ", stopped" + pending;
    01226         }
    01227         kdDebug(7001) << "    by " << c->instance->name()
    01228                       << " (" << c->count << " times)"
    01229                       << pending << endl;
    01230       }
    01231       if (e->m_entries.count()>0) {
    01232         kdDebug(7001) << "    dependent entries:" << endl;
    01233         Entry* d = e->m_entries.first();
    01234         for(;d; d = e->m_entries.next()) {
    01235           kdDebug(7001) << "      " << d->path << endl;
    01236         }
    01237       }
    01238     }
    01239   }
    01240 }
    01241
    01242
    01243 //
    01244 // Class KDirWatch
    01245 //
    01246
    01247 static KStaticDeleter<KDirWatch> sd_dw;
    01248 KDirWatch* KDirWatch::s_pSelf = 0L;
    01249
    01250 KDirWatch* KDirWatch::self()
    01251 {
    01252   if ( !s_pSelf ) {
    01253     sd_dw.setObject( s_pSelf, new KDirWatch );
    01254   }
    01255
    01256   return s_pSelf;
    01257 }
    01258
    01259 bool KDirWatch::exists()
    01260 {
    01261   return s_pSelf != 0;
    01262 }
    01263
    01264 KDirWatch::KDirWatch (QObject* parent, const char* name)
    01265   : QObject(parent,name)
    01266 {
    01267   if (!name) {
    01268     static int nameCounter = 0;
    01269
    01270     nameCounter++;
    01271     setName(QString("KDirWatch-%1").arg(nameCounter).ascii());
    01272   }
    01273
    01274   if (!dwp_self)
    01275     dwp_self = new KDirWatchPrivate;
    01276   d = dwp_self;
    01277   d->ref();
    01278
    01279   _isStopped = false;
    01280 }
    01281
    01282 KDirWatch::~KDirWatch()
    01283 {
    01284   if (d) d->removeEntries(this);
    01285   if ( d->deref() )
    01286   {
    01287     // delete it if it's the last one
    01288     delete d;
    01289     dwp_self = 0L;
    01290   }
    01291 }
    01292
    01293
    01294 // TODO: add watchFiles/recursive support
    01295 void KDirWatch::addDir( const QString& _path,
    01296                         bool watchFiles, bool recursive)
    01297 {
    01298   if (watchFiles || recursive) {
    01299     kdDebug(7001) << "addDir - recursive/watchFiles not supported in KDE 3.0"
    01300                   << endl;
    01301   }
    01302   if (d) d->addEntry(this, _path, 0, true);
    01303 }
    01304
    01305 void KDirWatch::addFile( const QString& _path )
    01306 {
    01307   if (d) d->addEntry(this, _path, 0, false);
    01308 }
    01309
    01310 QDateTime KDirWatch::ctime( const QString &_path )
    01311 {
    01312   KDirWatchPrivate::Entry* e = d->entry(_path);
    01313
    01314   if (!e)
    01315     return QDateTime();
    01316   else
    01317     return e->m_ctime;
    01318 }
    01319
    01320 void KDirWatch::removeDir( const QString& _path )
    01321 {
    01322   if (d) d->removeEntry(this, _path, 0);
    01323 }
    01324
    01325 void KDirWatch::removeFile( const QString& _path )
    01326 {
    01327   if (d) d->removeEntry(this, _path, 0);
    01328 }
    01329
    01330 bool KDirWatch::stopDirScan( const QString& _path )
    01331 {
    01332   if (d) {
    01333     KDirWatchPrivate::Entry *e = d->entry(_path);
    01334     if (e && e->isDir) return d->stopEntryScan(this, e);
    01335   }
    01336   return false;
    01337 }
    01338
    01339 bool KDirWatch::restartDirScan( const QString& _path )
    01340 {
    01341   if (d) {
    01342     KDirWatchPrivate::Entry *e = d->entry(_path);
    01343     if (e && e->isDir)
    01344       // restart without notifying pending events
    01345       return d->restartEntryScan(this, e, false);
    01346   }
    01347   return false;
    01348 }
    01349
    01350 void KDirWatch::stopScan()
    01351 {
    01352   if (d) d->stopScan(this);
    01353   _isStopped = true;
    01354 }
    01355
    01356 void KDirWatch::startScan( bool notify, bool skippedToo )
    01357 {
    01358   _isStopped = false;
    01359   if (d) d->startScan(this, notify, skippedToo);
    01360 }
    01361
    01362
    01363 bool KDirWatch::contains( const QString& _path ) const
    01364 {
    01365   KDirWatchPrivate::Entry* e = d->entry(_path);
    01366   if (!e)
    01367      return false;
    01368
    01369   KDirWatchPrivate::Client* c = e->m_clients.first();
    01370   for(;c;c=e->m_clients.next())
    01371     if (c->instance == this) return true;
    01372
    01373   return false;
    01374 }
    01375
    01376 void KDirWatch::statistics()
    01377 {
    01378   if (!dwp_self) {
    01379     kdDebug(7001) << "KDirWatch not used" << endl;
    01380     return;
    01381   }
    01382   dwp_self->statistics();
    01383 }
    01384
    01385
    01386 void KDirWatch::setCreated( const QString & _file )
    01387 {
    01388   kdDebug(7001) << name() << " emitting created " << _file << endl;
    01389   emit created( _file );
    01390 }
    01391
    01392 void KDirWatch::setDirty( const QString & _file )
    01393 {
    01394   kdDebug(7001) << name() << " emitting dirty " << _file << endl;
    01395   emit dirty( _file );
    01396 }
    01397
    01398 void KDirWatch::setDeleted( const QString & _file )
    01399 {
    01400   kdDebug(7001) << name() << " emitting deleted " << _file << endl;
    01401   emit deleted( _file );
    01402 }
    01403
    01404 #include "kdirwatch.moc"
    01405 #include "kdirwatch_p.moc"
    01406
    01407 //sven
    01408
    01409 // vim: sw=2 ts=8 et

This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Jan 28 13:13:25 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001