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