kio Library: kdirwatch.cpp Source File (kio Library)

| # kio Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Namespaces | 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 <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 (KIO::probably_slow_mounted(e->path))
    00524     useFreq(e, m_nfsPollInterval);
    00525   else
    00526     useFreq(e, m_PollInterval);
    00527
    00528   if (e->m_mode != StatMode) {
    00529     e->m_mode = StatMode;
    00530     statEntries++;
    00531
    00532     if ( statEntries == 1 ) {
    00533       // if this was first STAT entry (=timer was stopped)
    00534       timer->start(freq);      // then start the timer
    00535       kdDebug(7001) << " Started Polling Timer, freq " << freq << endl;
    00536     }
    00537   }
    00538
    00539   kdDebug(7001) << " Setup Stat (freq " << e->freq
    00540         << ") for " << e->path << endl;
    00541
    00542   return true;
    00543 }
    00544
    00545
    00546 /* If <instance> !=0, this KDirWatch instance wants to watch at <_path>,
    00547  * providing in <isDir> the type of the entry to be watched.
    00548  * Sometimes, entries are dependant on each other: if <sub_entry> !=0,
    00549  * this entry needs another entry to watch himself (when notExistent).
    00550  */
    00551 void KDirWatchPrivate::addEntry(KDirWatch* instance, const QString& _path,
    00552                 Entry* sub_entry, bool isDir)
    00553 {
    00554   QString path = _path;
    00555   if (path.startsWith("/dev/") || (path == "/dev"))
    00556     return; // Don't even go there.
    00557
    00558   if ( path.length() > 1 && path.right(1) == "/" )
    00559     path.truncate( path.length() - 1 );
    00560
    00561   EntryMap::Iterator it = m_mapEntries.find( path );
    00562   if ( it != m_mapEntries.end() )
    00563   {
    00564     if (sub_entry) {
    00565        (*it).m_entries.append(sub_entry);
    00566        kdDebug(7001) << "Added already watched Entry " << path
    00567              << " (for " << sub_entry->path << ")" << endl;
    00568 #ifdef HAVE_DNOTIFY
    00569        Entry* e = &(*it);
    00570        if( (e->m_mode == DNotifyMode) && (e->dn_fd > 0) ) {
    00571          int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
    00572          // if dependant is a file watch, we check for MODIFY & ATTRIB too
    00573          for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
    00574            if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB; break; }
    00575      if( fcntl(e->dn_fd, F_NOTIFY, mask) < 0) { // shouldn't happen
    00576        ::close(e->dn_fd);
    00577        e->m_mode = UnknownMode;
    00578        fd_Entry.remove(e->dn_fd);
    00579            e->dn_fd = 0;
    00580            useStat( e );
    00581          }
    00582        }
    00583 #endif
    00584     }
    00585     else {
    00586        (*it).addClient(instance);
    00587        kdDebug(7001) << "Added already watched Entry " << path
    00588              << " (now " <<  (*it).clients() << " clients)"
    00589              << QString(" [%1]").arg(instance->name()) << endl;
    00590     }
    00591     return;
    00592   }
    00593
    00594   // we have a new path to watch
    00595
    00596   KDE_struct_stat stat_buf;
    00597   QCString tpath = QFile::encodeName(path);
    00598   bool exists = (KDE_stat(tpath, &stat_buf) == 0);
    00599
    00600   Entry newEntry;
    00601   m_mapEntries.insert( path, newEntry );
    00602   // the insert does a copy, so we have to use <e> now
    00603   Entry* e = &(m_mapEntries[path]);
    00604
    00605   if (exists) {
    00606     e->isDir = S_ISDIR(stat_buf.st_mode);
    00607
    00608     if (e->isDir && !isDir)
    00609       qWarning("KDirWatch: %s is a directory. Use addDir!", path.ascii());
    00610     else if (!e->isDir && isDir)
    00611       qWarning("KDirWatch: %s is a file. Use addFile!", path.ascii());
    00612
    00613     e->m_ctime = stat_buf.st_ctime;
    00614     e->m_status = Normal;
    00615     e->m_nlink = stat_buf.st_nlink;
    00616   }
    00617   else {
    00618     e->isDir = isDir;
    00619     e->m_ctime = invalid_ctime;
    00620     e->m_status = NonExistent;
    00621     e->m_nlink = 0;
    00622   }
    00623
    00624   e->path = path;
    00625   if (sub_entry)
    00626      e->m_entries.append(sub_entry);
    00627   else
    00628     e->addClient(instance);
    00629
    00630   kdDebug(7001) << "Added " << (e->isDir ? "Dir ":"File ") << path
    00631         << (e->m_status == NonExistent ? " NotExisting" : "")
    00632         << (sub_entry ? QString(" for %1").arg(sub_entry->path) : QString(""))
    00633         << (instance ? QString(" [%1]").arg(instance->name()) : QString(""))
    00634         << endl;
    00635
    00636
    00637   // now setup the notification method
    00638   e->m_mode = UnknownMode;
    00639   e->msecLeft = 0;
    00640
    00641   if ( isNoisyFile( tpath ) )
    00642     return;
    00643
    00644 #if defined(HAVE_FAM)
    00645   if (useFAM(e)) return;
    00646 #endif
    00647
    00648 #ifdef HAVE_DNOTIFY
    00649   if (useDNotify(e)) return;
    00650 #endif
    00651
    00652   useStat(e);
    00653 }
    00654
    00655
    00656 void KDirWatchPrivate::removeEntry( KDirWatch* instance,
    00657                     const QString& _path, Entry* sub_entry )
    00658 {
    00659   Entry* e = entry(_path);
    00660   if (!e) {
    00661     kdWarning(7001) << "KDirWatch::removeDir can't handle '" << _path << "'" << endl;
    00662     return;
    00663   }
    00664
    00665   if (sub_entry)
    00666     e->m_entries.removeRef(sub_entry);
    00667   else
    00668     e->removeClient(instance);
    00669
    00670   if (e->m_clients.count() || e->m_entries.count())
    00671     return;
    00672
    00673   if (delayRemove) {
    00674     // removeList is allowed to contain any entry at most once
    00675     if (removeList.findRef(e)==-1)
    00676     removeList.append(e);
    00677     // now e->isValid() is false
    00678     return;
    00679   }
    00680
    00681 #ifdef HAVE_FAM
    00682   if (e->m_mode == FAMMode) {
    00683     if ( e->m_status == Normal) {
    00684       FAMCancelMonitor(&fc, &(e->fr) );
    00685       kdDebug(7001) << "Cancelled FAM (Req "
    00686             << FAMREQUEST_GETREQNUM(&(e->fr))
    00687             << ") for " << e->path << endl;
    00688     }
    00689     else {
    00690       if (e->isDir)
    00691     removeEntry(0, QDir::cleanDirPath(e->path+"/.."), e);
    00692       else
    00693     removeEntry(0, QFileInfo(e->path).dirPath(true), e);
    00694     }
    00695   }
    00696 #endif
    00697
    00698 #ifdef HAVE_DNOTIFY
    00699   if (e->m_mode == DNotifyMode) {
    00700     if (!e->isDir) {
    00701       removeEntry(0, QFileInfo(e->path).dirPath(true), e);
    00702     }
    00703     else { // isDir
    00704       // must close the FD.
    00705       if ( e->m_status == Normal) {
    00706     if (e->dn_fd) {
    00707       ::close(e->dn_fd);
    00708       fd_Entry.remove(e->dn_fd);
    00709
    00710       kdDebug(7001) << "Cancelled DNotify (fd " << e->dn_fd
    00711             << ") for " << e->path << endl;
    00712       e->dn_fd = 0;
    00713
    00714     }
    00715       }
    00716       else {
    00717     removeEntry(0, QDir::cleanDirPath(e->path+"/.."), e);
    00718       }
    00719     }
    00720   }
    00721 #endif
    00722
    00723   if (e->m_mode == StatMode) {
    00724     statEntries--;
    00725     if ( statEntries == 0 ) {
    00726       timer->stop(); // stop timer if lists are empty
    00727       kdDebug(7001) << " Stopped Polling Timer" << endl;
    00728     }
    00729   }
    00730
    00731   kdDebug(7001) << "Removed " << (e->isDir ? "Dir ":"File ") << e->path
    00732         << (sub_entry ? QString(" for %1").arg(sub_entry->path) : QString(""))
    00733         << (instance ? QString(" [%1]").arg(instance->name()) : QString(""))
    00734         << endl;
    00735   m_mapEntries.remove( e->path ); // <e> not valid any more
    00736 }
    00737
    00738
    00739 /* Called from KDirWatch destructor:
    00740  * remove <instance> as client from all entries
    00741  */
    00742 void KDirWatchPrivate::removeEntries( KDirWatch* instance )
    00743 {
    00744   QPtrList<Entry> list;
    00745   int minfreq = 3600000;
    00746
    00747   // put all entries where instance is a client in list
    00748   EntryMap::Iterator it = m_mapEntries.begin();
    00749   for( ; it != m_mapEntries.end(); ++it ) {
    00750     Client* c = (*it).m_clients.first();
    00751     for(;c;c=(*it).m_clients.next())
    00752       if (c->instance == instance) break;
    00753     if (c) {
    00754       c->count = 1; // forces deletion of instance as client
    00755       list.append(&(*it));
    00756     }
    00757     else if ( (*it).m_mode == StatMode && (*it).freq < minfreq )
    00758       minfreq = (*it).freq;
    00759   }
    00760
    00761   for(Entry* e=list.first();e;e=list.next())
    00762     removeEntry(instance, e->path, 0);
    00763
    00764   if (minfreq > freq) {
    00765     // we can decrease the global polling frequency
    00766     freq = minfreq;
    00767     if (timer->isActive()) timer->changeInterval(freq);
    00768     kdDebug(7001) << "Poll Freq now " << freq << " msec" << endl;
    00769   }
    00770 }
    00771
    00772 // instance ==0: stop scanning for all instances
    00773 bool KDirWatchPrivate::stopEntryScan( KDirWatch* instance, Entry* e)
    00774 {
    00775   int stillWatching = 0;
    00776   Client* c = e->m_clients.first();
    00777   for(;c;c=e->m_clients.next()) {
    00778     if (!instance || instance == c->instance)
    00779       c->watchingStopped = true;
    00780     else if (!c->watchingStopped)
    00781       stillWatching += c->count;
    00782   }
    00783
    00784   kdDebug(7001) << instance->name() << " stopped scanning " << e->path
    00785         << " (now " << stillWatching << " watchers)" << endl;
    00786
    00787   if (stillWatching == 0) {
    00788     // if nobody is interested, we don't watch
    00789     e->m_ctime = invalid_ctime; // invalid
    00790     //    e->m_status = Normal;
    00791   }
    00792   return true;
    00793 }
    00794
    00795 // instance ==0: start scanning for all instances
    00796 bool KDirWatchPrivate::restartEntryScan( KDirWatch* instance, Entry* e,
    00797                      bool notify)
    00798 {
    00799   int wasWatching = 0, newWatching = 0;
    00800   Client* c = e->m_clients.first();
    00801   for(;c;c=e->m_clients.next()) {
    00802     if (!c->watchingStopped)
    00803       wasWatching += c->count;
    00804     else if (!instance || instance == c->instance) {
    00805       c->watchingStopped = false;
    00806       newWatching += c->count;
    00807     }
    00808   }
    00809   if (newWatching == 0)
    00810     return false;
    00811
    00812   kdDebug(7001) << instance->name() << " restarted scanning " << e->path
    00813         << " (now " << wasWatching+newWatching << " watchers)" << endl;
    00814
    00815   // restart watching and emit pending events
    00816
    00817   int ev = NoChange;
    00818   if (wasWatching == 0) {
    00819     if (!notify) {
    00820       KDE_struct_stat stat_buf;
    00821       bool exists = (KDE_stat(QFile::encodeName(e->path), &stat_buf) == 0);
    00822       if (exists) {
    00823     e->m_ctime = stat_buf.st_ctime;
    00824     e->m_status = Normal;
    00825         e->m_nlink = stat_buf.st_nlink;
    00826       }
    00827       else {
    00828     e->m_ctime = invalid_ctime;
    00829     e->m_status = NonExistent;
    00830         e->m_nlink = 0;
    00831       }
    00832     }
    00833     e->msecLeft = 0;
    00834     ev = scanEntry(e);
    00835   }
    00836   emitEvent(e,ev);
    00837
    00838   return true;
    00839 }
    00840
    00841 // instance ==0: stop scanning for all instances
    00842 void KDirWatchPrivate::stopScan(KDirWatch* instance)
    00843 {
    00844   EntryMap::Iterator it = m_mapEntries.begin();
    00845   for( ; it != m_mapEntries.end(); ++it )
    00846     stopEntryScan(instance, &(*it));
    00847 }
    00848
    00849
    00850 void KDirWatchPrivate::startScan(KDirWatch* instance,
    00851                  bool notify, bool skippedToo )
    00852 {
    00853   if (!notify)
    00854     resetList(instance,skippedToo);
    00855
    00856   EntryMap::Iterator it = m_mapEntries.begin();
    00857   for( ; it != m_mapEntries.end(); ++it )
    00858     restartEntryScan(instance, &(*it), notify);
    00859
    00860   // timer should still be running when in polling mode
    00861 }
    00862
    00863
    00864 // clear all pending events, also from stopped
    00865 void KDirWatchPrivate::resetList( KDirWatch* /*instance*/,
    00866                   bool skippedToo )
    00867 {
    00868   EntryMap::Iterator it = m_mapEntries.begin();
    00869   for( ; it != m_mapEntries.end(); ++it ) {
    00870
    00871     Client* c = (*it).m_clients.first();
    00872     for(;c;c=(*it).m_clients.next())
    00873       if (!c->watchingStopped || skippedToo)
    00874     c->pending = NoChange;
    00875   }
    00876 }
    00877
    00878 // Return event happened on <e>
    00879 //
    00880 int KDirWatchPrivate::scanEntry(Entry* e)
    00881 {
    00882 #ifdef HAVE_FAM
    00883   if (e->m_mode == FAMMode) {
    00884     // we know nothing has changed, no need to stat
    00885     if(!e->dirty) return NoChange;
    00886     e->dirty = false;
    00887   }
    00888 #endif
    00889
    00890   // Shouldn't happen: Ignore "unknown" notification method
    00891   if (e->m_mode == UnknownMode) return NoChange;
    00892
    00893 #ifdef HAVE_DNOTIFY
    00894   if (e->m_mode == DNotifyMode) {
    00895     // we know nothing has changed, no need to stat
    00896     if(!e->dirty) return NoChange;
    00897     e->dirty = false;
    00898   }
    00899 #endif
    00900
    00901   if (e->m_mode == StatMode) {
    00902     // only scan if timeout on entry timer happens;
    00903     // e.g. when using 500msec global timer, a entry
    00904     // with freq=5000 is only watched every 10th time
    00905
    00906     e->msecLeft -= freq;
    00907     if (e->msecLeft>0) return NoChange;
    00908     e->msecLeft += e->freq;
    00909   }
    00910
    00911   KDE_struct_stat stat_buf;
    00912   bool exists = (KDE_stat(QFile::encodeName(e->path), &stat_buf) == 0);
    00913   if (exists) {
    00914
    00915     if (e->m_status == NonExistent) {
    00916       e->m_ctime = stat_buf.st_ctime;
    00917       e->m_status = Normal;
    00918       e->m_nlink = stat_buf.st_nlink;
    00919       return Created;
    00920     }
    00921
    00922     if ( (e->m_ctime != invalid_ctime) &&
    00923      ((stat_buf.st_ctime != e->m_ctime) ||
    00924       (stat_buf.st_nlink != (nlink_t) e->m_nlink)) ) {
    00925       e->m_ctime = stat_buf.st_ctime;
    00926       e->m_nlink = stat_buf.st_nlink;
    00927       return Changed;
    00928     }
    00929
    00930     return NoChange;
    00931   }
    00932
    00933   // dir/file doesn't exist
    00934
    00935   if (e->m_ctime == invalid_ctime)
    00936     return NoChange;
    00937
    00938   e->m_ctime = invalid_ctime;
    00939   e->m_nlink = 0;
    00940   e->m_status = NonExistent;
    00941
    00942   return Deleted;
    00943 }
    00944
    00945 /* Notify all interested KDirWatch instances about a given event on an entry
    00946  * and stored pending events. When watching is stopped, the event is
    00947  * added to the pending events.
    00948  */
    00949 void KDirWatchPrivate::emitEvent(Entry* e, int event, const QString &fileName)
    00950 {
    00951   QString path = e->path;
    00952   if (!fileName.isEmpty()) {
    00953     if (!QDir::isRelativePath(fileName))
    00954       path = fileName;
    00955     else
    00956 #ifdef Q_OS_UNIX
    00957       path += "/" + fileName;
    00958 #elif defined(Q_WS_WIN)
    00959       //current drive is passed instead of /
    00960       path += QDir::currentDirPath().left(2) + "/" + fileName;
    00961 #endif
    00962   }
    00963
    00964   Client* c = e->m_clients.first();
    00965   for(;c;c=e->m_clients.next()) {
    00966     if (c->instance==0 || c->count==0) continue;
    00967
    00968     if (c->watchingStopped) {
    00969       // add event to pending...
    00970       if (event == Changed)
    00971     c->pending |= event;
    00972       else if (event == Created || event == Deleted)
    00973     c->pending = event;
    00974       continue;
    00975     }
    00976     // not stopped
    00977     if (event == NoChange || event == Changed)
    00978       event |= c->pending;
    00979     c->pending = NoChange;
    00980     if (event == NoChange) continue;
    00981
    00982     if (event & Deleted) {
    00983       c->instance->setDeleted(path);
    00984       // emit only Deleted event...
    00985       continue;
    00986     }
    00987
    00988     if (event & Created) {
    00989       c->instance->setCreated(path);
    00990       // possible emit Change event after creation
    00991     }
    00992
    00993     if (event & Changed)
    00994       c->instance->setDirty(path);
    00995   }
    00996 }
    00997
    00998 // Remove entries which were marked to be removed
    00999 void KDirWatchPrivate::slotRemoveDelayed()
    01000 {
    01001   Entry* e;
    01002   delayRemove = false;
    01003   for(e=removeList.first();e;e=removeList.next())
    01004     removeEntry(0, e->path, 0);
    01005   removeList.clear();
    01006 }
    01007
    01008 /* Scan all entries to be watched for changes. This is done regularly
    01009  * when polling and once after a DNOTIFY signal. This is NOT used by FAM.
    01010  */
    01011 void KDirWatchPrivate::slotRescan()
    01012 {
    01013   EntryMap::Iterator it;
    01014
    01015   // People can do very long things in the slot connected to dirty(),
    01016   // like showing a message box. We don't want to keep polling during
    01017   // that time, otherwise the value of 'delayRemove' will be reset.
    01018   bool timerRunning = timer->isActive();
    01019   if ( timerRunning )
    01020     timer->stop();
    01021
    01022   // We delay deletions of entries this way.
    01023   // removeDir(), when called in slotDirty(), can cause a crash otherwise
    01024   delayRemove = true;
    01025
    01026 #ifdef HAVE_DNOTIFY
    01027   QPtrList<Entry> dList, cList;
    01028 #endif
    01029
    01030   if (rescan_all)
    01031   {
    01032     // mark all as dirty
    01033     it = m_mapEntries.begin();
    01034     for( ; it != m_mapEntries.end(); ++it )
    01035       (*it).dirty = true;
    01036     rescan_all = false;
    01037   }
    01038   else
    01039   {
    01040     // progate dirty flag to dependant entries (e.g. file watches)
    01041     it = m_mapEntries.begin();
    01042     for( ; it != m_mapEntries.end(); ++it )
    01043       if ( ((*it).m_mode == DNotifyMode) && (*it).dirty )
    01044         (*it).propagate_dirty();
    01045   }
    01046
    01047   it = m_mapEntries.begin();
    01048   for( ; it != m_mapEntries.end(); ++it ) {
    01049     // we don't check invalid entries (i.e. remove delayed)
    01050     if (!(*it).isValid()) continue;
    01051
    01052     int ev = scanEntry( &(*it) );
    01053
    01054 #ifdef HAVE_DNOTIFY
    01055     if ((*it).m_mode == DNotifyMode) {
    01056       if ((*it).isDir && (ev == Deleted)) {
    01057     dList.append( &(*it) );
    01058
    01059     // must close the FD.
    01060     if ((*it).dn_fd) {
    01061       ::close((*it).dn_fd);
    01062       fd_Entry.remove((*it).dn_fd);
    01063       (*it).dn_fd = 0;
    01064     }
    01065       }
    01066
    01067       else if ((*it).isDir && (ev == Created)) {
    01068     // For created, but yet without DNOTIFYing ...
    01069     if ( (*it).dn_fd == 0) {
    01070       cList.append( &(*it) );
    01071       if (! useDNotify( &(*it) )) {
    01072         // if DNotify setup fails...
    01073         useStat( &(*it) );
    01074       }
    01075     }
    01076       }
    01077     }
    01078 #endif
    01079
    01080     if ( ev != NoChange )
    01081       emitEvent( &(*it), ev);
    01082   }
    01083
    01084
    01085 #ifdef HAVE_DNOTIFY
    01086   // Scan parent of deleted directories for new creation
    01087   Entry* e;
    01088   for(e=dList.first();e;e=dList.next())
    01089     addEntry(0, QDir::cleanDirPath( e->path+"/.."), e, true);
    01090
    01091   // Remove watch of parent of new created directories
    01092   for(e=cList.first();e;e=cList.next())
    01093     removeEntry(0, QDir::cleanDirPath( e->path+"/.."), e);
    01094 #endif
    01095
    01096   if ( timerRunning )
    01097     timer->start(freq);
    01098
    01099   QTimer::singleShot(0, this, SLOT(slotRemoveDelayed()));
    01100 }
    01101
    01102 bool KDirWatchPrivate::isNoisyFile( const char * filename )
    01103 {
    01104   // $HOME/.X.err grows with debug output, so don't notify change
    01105   if ( *filename == '.') {
    01106     if (strncmp(filename, ".X.err", 6) == 0) return true;
    01107     if (strncmp(filename, ".xsession-errors", 16) == 0) return true;
    01108     // fontconfig updates the cache on every KDE app start
    01109     // (inclusive kio_thumbnail slaves)
    01110     if (strncmp(filename, ".fonts.cache", 12) == 0) return true;
    01111   }
    01112
    01113   return false;
    01114 }
    01115
    01116 #ifdef HAVE_FAM
    01117 void KDirWatchPrivate::famEventReceived()
    01118 {
    01119   static FAMEvent fe;
    01120
    01121   delayRemove = true;
    01122
    01123   while(use_fam && FAMPending(&fc)) {
    01124     if (FAMNextEvent(&fc, &fe) == -1) {
    01125       kdWarning(7001) << "FAM connection problem, switching to polling."
    01126               << endl;
    01127       use_fam = false;
    01128       delete sn; sn = 0;
    01129
    01130       // Replace all FAMMode entries with DNotify/Stat
    01131       EntryMap::Iterator it;
    01132       it = m_mapEntries.begin();
    01133       for( ; it != m_mapEntries.end(); ++it )
    01134     if ((*it).m_mode == FAMMode && (*it).m_clients.count()>0) {
    01135 #ifdef HAVE_DNOTIFY
    01136       if (useDNotify( &(*it) )) continue;
    01137 #endif
    01138       useStat( &(*it) );
    01139     }
    01140     }
    01141     else
    01142       checkFAMEvent(&fe);
    01143   }
    01144
    01145   QTimer::singleShot(0, this, SLOT(slotRemoveDelayed()));
    01146 }
    01147
    01148 void KDirWatchPrivate::checkFAMEvent(FAMEvent* fe)
    01149 {
    01150   // Don't be too verbose ;-)
    01151   if ((fe->code == FAMExists) ||
    01152       (fe->code == FAMEndExist) ||
    01153       (fe->code == FAMAcknowledge)) return;
    01154
    01155   if ( isNoisyFile( fe->filename ) )
    01156     return;
    01157
    01158   Entry* e = 0;
    01159   EntryMap::Iterator it = m_mapEntries.begin();
    01160   for( ; it != m_mapEntries.end(); ++it )
    01161     if (FAMREQUEST_GETREQNUM(&( (*it).fr )) ==
    01162        FAMREQUEST_GETREQNUM(&(fe->fr)) ) {
    01163       e = &(*it);
    01164       break;
    01165     }
    01166
    01167   // Entry* e = static_cast<Entry*>(fe->userdata);
    01168
    01169 #if 0 // #88538
    01170   kdDebug(7001) << "Processing FAM event ("
    01171         << ((fe->code == FAMChanged) ? "FAMChanged" :
    01172             (fe->code == FAMDeleted) ? "FAMDeleted" :
    01173             (fe->code == FAMStartExecuting) ? "FAMStartExecuting" :
    01174             (fe->code == FAMStopExecuting) ? "FAMStopExecuting" :
    01175             (fe->code == FAMCreated) ? "FAMCreated" :
    01176             (fe->code == FAMMoved) ? "FAMMoved" :
    01177             (fe->code == FAMAcknowledge) ? "FAMAcknowledge" :
    01178             (fe->code == FAMExists) ? "FAMExists" :
    01179             (fe->code == FAMEndExist) ? "FAMEndExist" : "Unknown Code")
    01180         << ", " << fe->filename
    01181         << ", Req " << FAMREQUEST_GETREQNUM(&(fe->fr))
    01182         << ")" << endl;
    01183 #endif
    01184
    01185   if (!e) {
    01186     // this happens e.g. for FAMAcknowledge after deleting a dir...
    01187     //    kdDebug(7001) << "No entry for FAM event ?!" << endl;
    01188     return;
    01189   }
    01190
    01191   if (e->m_status == NonExistent) {
    01192     kdDebug(7001) << "FAM event for nonExistent entry " << e->path << endl;
    01193     return;
    01194   }
    01195
    01196   // Delayed handling. This rechecks changes with own stat calls.
    01197   e->dirty = true;
    01198   if (!rescan_timer.isActive())
    01199     rescan_timer.start(m_PollInterval, true);
    01200
    01201   // needed FAM control actions on FAM events
    01202   if (e->isDir)
    01203     switch (fe->code)
    01204     {
    01205       case FAMDeleted:
    01206        // file absolute: watched dir
    01207         if (!QDir::isRelativePath(fe->filename))
    01208         {
    01209           // a watched directory was deleted
    01210
    01211           e->m_status = NonExistent;
    01212           FAMCancelMonitor(&fc, &(e->fr) ); // needed ?
    01213           kdDebug(7001) << "Cancelled FAMReq "
    01214                         << FAMREQUEST_GETREQNUM(&(e->fr))
    01215                         << " for " << e->path << endl;
    01216           // Scan parent for a new creation
    01217           addEntry(0, QDir::cleanDirPath( e->path+"/.."), e, true);
    01218         }
    01219         break;
    01220
    01221       case FAMCreated: {
    01222           // check for creation of a directory we have to watch
    01223           Entry *sub_entry = e->m_entries.first();
    01224           for(;sub_entry; sub_entry = e->m_entries.next())
    01225             if (sub_entry->path == e->path + "/" + fe->filename) break;
    01226           if (sub_entry && sub_entry->isDir) {
    01227             QString path = e->path;
    01228             removeEntry(0,e->path,sub_entry); // <e> can be invalid here!!
    01229             sub_entry->m_status = Normal;
    01230             if (!useFAM(sub_entry))
    01231               useStat(sub_entry);
    01232           }
    01233           break;
    01234         }
    01235
    01236       default:
    01237         break;
    01238     }
    01239 }
    01240 #else
    01241 void KDirWatchPrivate::famEventReceived() {}
    01242 #endif
    01243
    01244
    01245 void KDirWatchPrivate::statistics()
    01246 {
    01247   EntryMap::Iterator it;
    01248
    01249   kdDebug(7001) << "Entries watched:" << endl;
    01250   if (m_mapEntries.count()==0) {
    01251     kdDebug(7001) << "  None." << endl;
    01252   }
    01253   else {
    01254     it = m_mapEntries.begin();
    01255     for( ; it != m_mapEntries.end(); ++it ) {
    01256       Entry* e = &(*it);
    01257       kdDebug(7001) << "  " << e->path << " ("
    01258             << ((e->m_status==Normal)?"":"Nonexistent ")
    01259             << (e->isDir ? "Dir":"File") << ", using "
    01260             << ((e->m_mode == FAMMode) ? "FAM" :
    01261             (e->m_mode == DNotifyMode) ? "DNotify" :
    01262             (e->m_mode == StatMode) ? "Stat" : "Unknown Method")
    01263             << ")" << endl;
    01264
    01265       Client* c = e->m_clients.first();
    01266       for(;c; c = e->m_clients.next()) {
    01267     QString pending;
    01268     if (c->watchingStopped) {
    01269       if (c->pending & Deleted) pending += "deleted ";
    01270       if (c->pending & Created) pending += "created ";
    01271       if (c->pending & Changed) pending += "changed ";
    01272       if (!pending.isEmpty()) pending = " (pending: " + pending + ")";
    01273       pending = ", stopped" + pending;
    01274     }
    01275     kdDebug(7001) << "    by " << c->instance->name()
    01276               << " (" << c->count << " times)"
    01277               << pending << endl;
    01278       }
    01279       if (e->m_entries.count()>0) {
    01280     kdDebug(7001) << "    dependent entries:" << endl;
    01281     Entry* d = e->m_entries.first();
    01282     for(;d; d = e->m_entries.next()) {
    01283       kdDebug(7001) << "      " << d->path << endl;
    01284     }
    01285       }
    01286     }
    01287   }
    01288 }
    01289
    01290
    01291 //
    01292 // Class KDirWatch
    01293 //
    01294
    01295 static KStaticDeleter<KDirWatch> sd_dw;
    01296 KDirWatch* KDirWatch::s_pSelf = 0L;
    01297
    01298 KDirWatch* KDirWatch::self()
    01299 {
    01300   if ( !s_pSelf ) {
    01301     sd_dw.setObject( s_pSelf, new KDirWatch );
    01302   }
    01303
    01304   return s_pSelf;
    01305 }
    01306
    01307 bool KDirWatch::exists()
    01308 {
    01309   return s_pSelf != 0;
    01310 }
    01311
    01312 KDirWatch::KDirWatch (QObject* parent, const char* name)
    01313   : QObject(parent,name)
    01314 {
    01315   if (!name) {
    01316     static int nameCounter = 0;
    01317
    01318     nameCounter++;
    01319     setName(QString("KDirWatch-%1").arg(nameCounter).ascii());
    01320   }
    01321
    01322   if (!dwp_self)
    01323     dwp_self = new KDirWatchPrivate;
    01324   d = dwp_self;
    01325   d->ref();
    01326
    01327   _isStopped = false;
    01328 }
    01329
    01330 KDirWatch::~KDirWatch()
    01331 {
    01332   if (d) d->removeEntries(this);
    01333   if ( d->deref() )
    01334   {
    01335     // delete it if it's the last one
    01336     delete d;
    01337     dwp_self = 0L;
    01338   }
    01339 }
    01340
    01341
    01342 // TODO: add watchFiles/recursive support
    01343 void KDirWatch::addDir( const QString& _path,
    01344             bool watchFiles, bool recursive)
    01345 {
    01346   if (watchFiles || recursive) {
    01347     kdDebug(7001) << "addDir - recursive/watchFiles not supported yet in KDE 3.x" << endl;
    01348   }
    01349   if (d) d->addEntry(this, _path, 0, true);
    01350 }
    01351
    01352 void KDirWatch::addFile( const QString& _path )
    01353 {
    01354   if (d) d->addEntry(this, _path, 0, false);
    01355 }
    01356
    01357 QDateTime KDirWatch::ctime( const QString &_path )
    01358 {
    01359   KDirWatchPrivate::Entry* e = d->entry(_path);
    01360
    01361   if (!e)
    01362     return QDateTime();
    01363
    01364   QDateTime result;
    01365   result.setTime_t(e->m_ctime);
    01366   return result;
    01367 }
    01368
    01369 void KDirWatch::removeDir( const QString& _path )
    01370 {
    01371   if (d) d->removeEntry(this, _path, 0);
    01372 }
    01373
    01374 void KDirWatch::removeFile( const QString& _path )
    01375 {
    01376   if (d) d->removeEntry(this, _path, 0);
    01377 }
    01378
    01379 bool KDirWatch::stopDirScan( const QString& _path )
    01380 {
    01381   if (d) {
    01382     KDirWatchPrivate::Entry *e = d->entry(_path);
    01383     if (e && e->isDir) return d->stopEntryScan(this, e);
    01384   }
    01385   return false;
    01386 }
    01387
    01388 bool KDirWatch::restartDirScan( const QString& _path )
    01389 {
    01390   if (d) {
    01391     KDirWatchPrivate::Entry *e = d->entry(_path);
    01392     if (e && e->isDir)
    01393       // restart without notifying pending events
    01394       return d->restartEntryScan(this, e, false);
    01395   }
    01396   return false;
    01397 }
    01398
    01399 void KDirWatch::stopScan()
    01400 {
    01401   if (d) d->stopScan(this);
    01402   _isStopped = true;
    01403 }
    01404
    01405 void KDirWatch::startScan( bool notify, bool skippedToo )
    01406 {
    01407   _isStopped = false;
    01408   if (d) d->startScan(this, notify, skippedToo);
    01409 }
    01410
    01411
    01412 bool KDirWatch::contains( const QString& _path ) const
    01413 {
    01414   KDirWatchPrivate::Entry* e = d->entry(_path);
    01415   if (!e)
    01416      return false;
    01417
    01418   KDirWatchPrivate::Client* c = e->m_clients.first();
    01419   for(;c;c=e->m_clients.next())
    01420     if (c->instance == this) return true;
    01421
    01422   return false;
    01423 }
    01424
    01425 void KDirWatch::statistics()
    01426 {
    01427   if (!dwp_self) {
    01428     kdDebug(7001) << "KDirWatch not used" << endl;
    01429     return;
    01430   }
    01431   dwp_self->statistics();
    01432 }
    01433
    01434
    01435 void KDirWatch::setCreated( const QString & _file )
    01436 {
    01437   kdDebug(7001) << name() << " emitting created " << _file << endl;
    01438   emit created( _file );
    01439 }
    01440
    01441 void KDirWatch::setDirty( const QString & _file )
    01442 {
    01443   kdDebug(7001) << name() << " emitting dirty " << _file << endl;
    01444   emit dirty( _file );
    01445 }
    01446
    01447 void KDirWatch::setDeleted( const QString & _file )
    01448 {
    01449   kdDebug(7001) << name() << " emitting deleted " << _file << endl;
    01450   emit deleted( _file );
    01451 }
    01452
    01453 KDirWatch::Method KDirWatch::internalMethod()
    01454 {
    01455 #ifdef HAVE_FAM
    01456   if (d->use_fam)
    01457      return KDirWatch::FAM;
    01458 #endif
    01459 #ifdef HAVE_DNOTIFY
    01460   if (d->supports_dnotify)
    01461      return KDirWatch::DNotify;
    01462 #endif
    01463   return KDirWatch::Stat;
    01464 }
    01465
    01466
    01467 #include "kdirwatch.moc"
    01468 #include "kdirwatch_p.moc"
    01469
    01470 //sven
    01471
    01472 // vim: sw=2 ts=8 et

This file is part of the documentation for kio Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:23:57 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003