summaryrefslogtreecommitdiffabout
authorzautrix <zautrix>2004-08-08 12:30:41 (UTC)
committer zautrix <zautrix>2004-08-08 12:30:41 (UTC)
commit75138e84a2271f8929478853151a6ed4a73fb2cc (patch) (unidiff)
tree804b3277bf2e4c4e371611677481803f527bf90e
parent4cc869512488b72304c7cbb5526c6f4cc957e677 (diff)
downloadkdepimpi-75138e84a2271f8929478853151a6ed4a73fb2cc.zip
kdepimpi-75138e84a2271f8929478853151a6ed4a73fb2cc.tar.gz
kdepimpi-75138e84a2271f8929478853151a6ed4a73fb2cc.tar.bz2
Fixed kdirwatch
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--microkde/kio/kio/kdirwatch.cpp3
1 files changed, 2 insertions, 1 deletions
diff --git a/microkde/kio/kio/kdirwatch.cpp b/microkde/kio/kio/kdirwatch.cpp
index 98d24e0..1596d1f 100644
--- a/microkde/kio/kio/kdirwatch.cpp
+++ b/microkde/kio/kio/kdirwatch.cpp
@@ -1,1442 +1,1443 @@
1// -*- c-basic-offset: 2 -*- 1// -*- c-basic-offset: 2 -*-
2/* This file is part of the KDE libraries 2/* This file is part of the KDE libraries
3 Copyright (C) 1998 Sven Radej <sven@lisa.exp.univie.ac.at> 3 Copyright (C) 1998 Sven Radej <sven@lisa.exp.univie.ac.at>
4 4
5 This library is free software; you can redistribute it and/or 5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public 6 modify it under the terms of the GNU Library General Public
7 License version 2 as published by the Free Software Foundation. 7 License version 2 as published by the Free Software Foundation.
8 8
9 This library is distributed in the hope that it will be useful, 9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of 10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details. 12 Library General Public License for more details.
13 13
14 You should have received a copy of the GNU Library General Public License 14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to 15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 16 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 Boston, MA 02111-1307, USA. 17 Boston, MA 02111-1307, USA.
18*/ 18*/
19 19
20/* 20/*
21Enhanced Version of the file for platform independent KDE tools. 21Enhanced Version of the file for platform independent KDE tools.
22Copyright (c) 2004 Ulf Schenk 22Copyright (c) 2004 Ulf Schenk
23 23
24$Id$ 24$Id$
25*/ 25*/
26 26
27 27
28// CHANGES: 28// CHANGES:
29// Februar 2002 - Add file watching and remote mount check for STAT 29// Februar 2002 - Add file watching and remote mount check for STAT
30// Mar 30, 2001 - Native support for Linux dir change notification. 30// Mar 30, 2001 - Native support for Linux dir change notification.
31// Jan 28, 2000 - Usage of FAM service on IRIX (Josef.Weidendorfer@in.tum.de) 31// Jan 28, 2000 - Usage of FAM service on IRIX (Josef.Weidendorfer@in.tum.de)
32// May 24. 1998 - List of times introduced, and some bugs are fixed. (sven) 32// May 24. 1998 - List of times introduced, and some bugs are fixed. (sven)
33// May 23. 1998 - Removed static pointer - you can have more instances. 33// May 23. 1998 - Removed static pointer - you can have more instances.
34// It was Needed for KRegistry. KDirWatch now emits signals and doesn't 34// It was Needed for KRegistry. KDirWatch now emits signals and doesn't
35// call (or need) KFM. No more URL's - just plain paths. (sven) 35// call (or need) KFM. No more URL's - just plain paths. (sven)
36// Mar 29. 1998 - added docs, stop/restart for particular Dirs and 36// Mar 29. 1998 - added docs, stop/restart for particular Dirs and
37// deep copies for list of dirs. (sven) 37// deep copies for list of dirs. (sven)
38// Mar 28. 1998 - Created. (sven) 38// Mar 28. 1998 - Created. (sven)
39 39
40 40
41//US #include <config.h> 41//US #include <config.h>
42 42
43#ifdef HAVE_DNOTIFY 43#ifdef HAVE_DNOTIFY
44#include <unistd.h> 44#include <unistd.h>
45#include <time.h> 45#include <time.h>
46#include <fcntl.h> 46#include <fcntl.h>
47#include <signal.h> 47#include <signal.h>
48#include <errno.h> 48#include <errno.h>
49#endif 49#endif
50 50
51#include <sys/stat.h> 51#include <sys/stat.h>
52#include <assert.h> 52#include <assert.h>
53#include <qdir.h> 53#include <qdir.h>
54#include <qfile.h> 54#include <qfile.h>
55#include <qintdict.h> 55#include <qintdict.h>
56#include <qptrlist.h> 56#include <qptrlist.h>
57#include <qsocketnotifier.h> 57#include <qsocketnotifier.h>
58#include <qstringlist.h> 58#include <qstringlist.h>
59#include <qtimer.h> 59#include <qtimer.h>
60 60
61#include <kapplication.h> 61#include <kapplication.h>
62#include <kdebug.h> 62#include <kdebug.h>
63#include <kconfig.h> 63#include <kconfig.h>
64#include <kconfigbase.h> 64#include <kconfigbase.h>
65#include <kglobal.h> 65#include <kglobal.h>
66#include <kstaticdeleter.h> 66#include <kstaticdeleter.h>
67 67
68#include "kdirwatch.h" 68#include "kdirwatch.h"
69#include "kdirwatch_p.h" 69#include "kdirwatch_p.h"
70//US #include "global.h" // KIO::probably_slow_mounted 70//US #include "global.h" // KIO::probably_slow_mounted
71 71
72#define NO_NOTIFY (time_t) 0 72#define NO_NOTIFY (time_t) 0
73 73
74static KDirWatchPrivate* dwp_self = 0; 74static KDirWatchPrivate* dwp_self = 0;
75 75
76#ifdef HAVE_DNOTIFY 76#ifdef HAVE_DNOTIFY
77 77
78#include <sys/utsname.h> 78#include <sys/utsname.h>
79 79
80static int dnotify_signal = 0; 80static int dnotify_signal = 0;
81 81
82/* DNOTIFY signal handler 82/* DNOTIFY signal handler
83 * 83 *
84 * As this is called asynchronously, only a flag is set and 84 * As this is called asynchronously, only a flag is set and
85 * a rescan is requested. 85 * a rescan is requested.
86 * This is done by writing into a pipe to trigger a QSocketNotifier 86 * This is done by writing into a pipe to trigger a QSocketNotifier
87 * watching on this pipe: a timer is started and after a timeout, 87 * watching on this pipe: a timer is started and after a timeout,
88 * the rescan is done. 88 * the rescan is done.
89 */ 89 */
90void KDirWatchPrivate::dnotify_handler(int, siginfo_t *si, void *) 90void KDirWatchPrivate::dnotify_handler(int, siginfo_t *si, void *)
91{ 91{
92 // write might change errno, we have to save it and restore it 92 // write might change errno, we have to save it and restore it
93 // (Richard Stevens, Advanced programming in the Unix Environment) 93 // (Richard Stevens, Advanced programming in the Unix Environment)
94 int saved_errno = errno; 94 int saved_errno = errno;
95 95
96 Entry* e = (dwp_self) ? dwp_self->fd_Entry.find(si->si_fd) :0; 96 Entry* e = (dwp_self) ? dwp_self->fd_Entry.find(si->si_fd) :0;
97 97
98// kdDebug(7001) << "DNOTIFY Handler: fd " << si->si_fd << " path " 98// kdDebug(7001) << "DNOTIFY Handler: fd " << si->si_fd << " path "
99 // << QString(e ? e->path:"unknown") << endl; 99 // << QString(e ? e->path:"unknown") << endl;
100 100
101 if(!e || e->dn_fd != si->si_fd) { 101 if(!e || e->dn_fd != si->si_fd) {
102 qDebug("fatal error in KDirWatch"); 102 qDebug("fatal error in KDirWatch");
103 } else 103 } else
104 e->dn_dirty = true; 104 e->dn_dirty = true;
105 105
106 char c = 0; 106 char c = 0;
107 write(dwp_self->mPipe[1], &c, 1); 107 write(dwp_self->mPipe[1], &c, 1);
108 errno = saved_errno; 108 errno = saved_errno;
109} 109}
110 110
111static struct sigaction old_sigio_act; 111static struct sigaction old_sigio_act;
112/* DNOTIFY SIGIO signal handler 112/* DNOTIFY SIGIO signal handler
113 * 113 *
114 * When the kernel queue for the dnotify_signal overflows, a SIGIO is send. 114 * When the kernel queue for the dnotify_signal overflows, a SIGIO is send.
115 */ 115 */
116void KDirWatchPrivate::dnotify_sigio_handler(int sig, siginfo_t *si, void *p) 116void KDirWatchPrivate::dnotify_sigio_handler(int sig, siginfo_t *si, void *p)
117{ 117{
118 // write might change errno, we have to save it and restore it 118 // write might change errno, we have to save it and restore it
119 // (Richard Stevens, Advanced programming in the Unix Environment) 119 // (Richard Stevens, Advanced programming in the Unix Environment)
120 int saved_errno = errno; 120 int saved_errno = errno;
121 121
122 if (dwp_self) 122 if (dwp_self)
123 dwp_self->rescan_all = true; 123 dwp_self->rescan_all = true;
124 124
125 char c = 0; 125 char c = 0;
126 write(dwp_self->mPipe[1], &c, 1); 126 write(dwp_self->mPipe[1], &c, 1);
127 127
128 errno = saved_errno; 128 errno = saved_errno;
129 129
130 // Call previous signal handler 130 // Call previous signal handler
131 if (old_sigio_act.sa_flags & SA_SIGINFO) 131 if (old_sigio_act.sa_flags & SA_SIGINFO)
132 { 132 {
133 if (old_sigio_act.sa_sigaction) 133 if (old_sigio_act.sa_sigaction)
134 (*old_sigio_act.sa_sigaction)(sig, si, p); 134 (*old_sigio_act.sa_sigaction)(sig, si, p);
135 } 135 }
136 else 136 else
137 { 137 {
138 if ((old_sigio_act.sa_handler != SIG_DFL) && 138 if ((old_sigio_act.sa_handler != SIG_DFL) &&
139 (old_sigio_act.sa_handler != SIG_IGN)) 139 (old_sigio_act.sa_handler != SIG_IGN))
140 (*old_sigio_act.sa_handler)(sig); 140 (*old_sigio_act.sa_handler)(sig);
141 } 141 }
142} 142}
143#endif 143#endif
144 144
145 145
146// 146//
147// Class KDirWatchPrivate (singleton) 147// Class KDirWatchPrivate (singleton)
148// 148//
149 149
150/* All entries (files/directories) to be watched in the 150/* All entries (files/directories) to be watched in the
151 * application (coming from multiple KDirWatch instances) 151 * application (coming from multiple KDirWatch instances)
152 * are registered in a single KDirWatchPrivate instance. 152 * are registered in a single KDirWatchPrivate instance.
153 * 153 *
154 * At the moment, the following methods for file watching 154 * At the moment, the following methods for file watching
155 * are supported: 155 * are supported:
156 * - Polling: All files to be watched are polled regularly 156 * - Polling: All files to be watched are polled regularly
157 * using stat (more precise: QFileInfo.lastModified()). 157 * using stat (more precise: QFileInfo.lastModified()).
158 * The polling frequency is determined from global kconfig 158 * The polling frequency is determined from global kconfig
159 * settings, defaulting to 500 ms for local directories 159 * settings, defaulting to 500 ms for local directories
160 * and 5000 ms for remote mounts 160 * and 5000 ms for remote mounts
161 * - FAM (File Alternation Monitor): first used on IRIX, SGI 161 * - FAM (File Alternation Monitor): first used on IRIX, SGI
162 * has ported this method to LINUX. It uses a kernel part 162 * has ported this method to LINUX. It uses a kernel part
163 * (IMON, sending change events to /dev/imon) and a user 163 * (IMON, sending change events to /dev/imon) and a user
164 * level damon (fam), to which applications connect for 164 * level damon (fam), to which applications connect for
165 * notification of file changes. For NFS, the fam damon 165 * notification of file changes. For NFS, the fam damon
166 * on the NFS server machine is used; if IMON is not built 166 * on the NFS server machine is used; if IMON is not built
167 * into the kernel, fam uses polling for local files. 167 * into the kernel, fam uses polling for local files.
168 * - DNOTIFY: In late LINUX 2.3.x, directory notification was 168 * - DNOTIFY: In late LINUX 2.3.x, directory notification was
169 * introduced. By opening a directory, you can request for 169 * introduced. By opening a directory, you can request for
170 * UNIX signals to be sent to the process when a directory 170 * UNIX signals to be sent to the process when a directory
171 * is changed. 171 * is changed.
172 */ 172 */
173 173
174KDirWatchPrivate::KDirWatchPrivate() 174KDirWatchPrivate::KDirWatchPrivate()
175{ 175{
176 timer = new QTimer(this); 176 timer = new QTimer(this);
177 connect (timer, SIGNAL(timeout()), this, SLOT(slotRescan())); 177 connect (timer, SIGNAL(timeout()), this, SLOT(slotRescan()));
178 freq = 3600000; // 1 hour as upper bound 178 freq = 3600000; // 1 hour as upper bound
179 statEntries = 0; 179 statEntries = 0;
180 delayRemove = false; 180 delayRemove = false;
181 m_ref = 0; 181 m_ref = 0;
182 182
183//US KConfigGroup config(KGlobal::config(), QCString("DirWatch")); 183//US KConfigGroup config(KGlobal::config(), QCString("DirWatch"));
184//US m_nfsPollInterval = config.readNumEntry("NFSPollInterval", 5000); 184//US m_nfsPollInterval = config.readNumEntry("NFSPollInterval", 5000);
185//US m_PollInterval = config.readNumEntry("PollInterval", 500); 185//US m_PollInterval = config.readNumEntry("PollInterval", 500);
186 KConfig *config = KGlobal::config(); 186 KConfig *config = KGlobal::config();
187 KConfigGroupSaver saver( config, QCString("DirWatch") ); 187 KConfigGroupSaver saver( config, QCString("DirWatch") );
188 188
189 m_nfsPollInterval = config->readNumEntry("NFSPollInterval", 5000); 189 m_nfsPollInterval = config->readNumEntry("NFSPollInterval", 5000);
190 m_PollInterval = config->readNumEntry("PollInterval", 500); 190 m_PollInterval = config->readNumEntry("PollInterval", 500);
191 191
192 192
193 QString available("Stat"); 193 QString available("Stat");
194 194
195#ifdef HAVE_FAM 195#ifdef HAVE_FAM
196 // It's possible that FAM server can't be started 196 // It's possible that FAM server can't be started
197 if (FAMOpen(&fc) ==0) { 197 if (FAMOpen(&fc) ==0) {
198 available += ", FAM"; 198 available += ", FAM";
199 use_fam=true; 199 use_fam=true;
200 sn = new QSocketNotifier( FAMCONNECTION_GETFD(&fc), 200 sn = new QSocketNotifier( FAMCONNECTION_GETFD(&fc),
201 QSocketNotifier::Read, this); 201 QSocketNotifier::Read, this);
202 connect( sn, SIGNAL(activated(int)), 202 connect( sn, SIGNAL(activated(int)),
203 this, SLOT(famEventReceived()) ); 203 this, SLOT(famEventReceived()) );
204 } 204 }
205 else { 205 else {
206 kdDebug(7001) << "Can't use FAM (fam daemon not running?)" << endl; 206 kdDebug(7001) << "Can't use FAM (fam daemon not running?)" << endl;
207 use_fam=false; 207 use_fam=false;
208 } 208 }
209#endif 209#endif
210 210
211#ifdef HAVE_DNOTIFY 211#ifdef HAVE_DNOTIFY
212 supports_dnotify = true; // not guilty until proven guilty 212 supports_dnotify = true; // not guilty until proven guilty
213 rescan_all = false; 213 rescan_all = false;
214 struct utsname uts; 214 struct utsname uts;
215 int major, minor, patch; 215 int major, minor, patch;
216 if (uname(&uts) < 0) 216 if (uname(&uts) < 0)
217 supports_dnotify = false; // *shrug* 217 supports_dnotify = false; // *shrug*
218 else if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) != 3) 218 else if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) != 3)
219 supports_dnotify = false; // *shrug* 219 supports_dnotify = false; // *shrug*
220 else if( major * 1000000 + minor * 1000 + patch < 2004019 ) { // <2.4.19 220 else if( major * 1000000 + minor * 1000 + patch < 2004019 ) { // <2.4.19
221 kdDebug(7001) << "Can't use DNotify, Linux kernel too old" << endl; 221 kdDebug(7001) << "Can't use DNotify, Linux kernel too old" << endl;
222 supports_dnotify = false; 222 supports_dnotify = false;
223 } 223 }
224 224
225 if( supports_dnotify ) { 225 if( supports_dnotify ) {
226 available += ", DNotify"; 226 available += ", DNotify";
227 227
228 pipe(mPipe); 228 pipe(mPipe);
229 fcntl(mPipe[0], F_SETFD, FD_CLOEXEC); 229 fcntl(mPipe[0], F_SETFD, FD_CLOEXEC);
230 fcntl(mPipe[1], F_SETFD, FD_CLOEXEC); 230 fcntl(mPipe[1], F_SETFD, FD_CLOEXEC);
231 mSn = new QSocketNotifier( mPipe[0], QSocketNotifier::Read, this); 231 mSn = new QSocketNotifier( mPipe[0], QSocketNotifier::Read, this);
232 connect(mSn, SIGNAL(activated(int)), this, SLOT(slotActivated())); 232 connect(mSn, SIGNAL(activated(int)), this, SLOT(slotActivated()));
233 connect(&mTimer, SIGNAL(timeout()), this, SLOT(slotRescan())); 233 connect(&mTimer, SIGNAL(timeout()), this, SLOT(slotRescan()));
234 struct sigaction act; 234 struct sigaction act;
235 act.sa_sigaction = KDirWatchPrivate::dnotify_handler; 235 act.sa_sigaction = KDirWatchPrivate::dnotify_handler;
236 sigemptyset(&act.sa_mask); 236 sigemptyset(&act.sa_mask);
237 act.sa_flags = SA_SIGINFO; 237 act.sa_flags = SA_SIGINFO;
238#ifdef SA_RESTART 238#ifdef SA_RESTART
239 act.sa_flags |= SA_RESTART; 239 act.sa_flags |= SA_RESTART;
240#endif 240#endif
241 if( dnotify_signal == 0 ) 241 if( dnotify_signal == 0 )
242 dnotify_signal = SIGRTMIN + 8; 242 dnotify_signal = SIGRTMIN + 8;
243 sigaction(dnotify_signal, &act, NULL); 243 sigaction(dnotify_signal, &act, NULL);
244 244
245 act.sa_sigaction = KDirWatchPrivate::dnotify_sigio_handler; 245 act.sa_sigaction = KDirWatchPrivate::dnotify_sigio_handler;
246 sigaction(SIGIO, &act, &old_sigio_act); 246 sigaction(SIGIO, &act, &old_sigio_act);
247 } 247 }
248#endif 248#endif
249 249
250 kdDebug(7001) << "Available methods: " << available << endl; 250 kdDebug(7001) << "Available methods: " << available << endl;
251} 251}
252 252
253/* This should never be called, but doesn't harm */ 253/* This should never be called, but doesn't harm */
254KDirWatchPrivate::~KDirWatchPrivate() 254KDirWatchPrivate::~KDirWatchPrivate()
255{ 255{
256 timer->stop(); 256 timer->stop();
257 257
258 /* remove all entries being watched */ 258 /* remove all entries being watched */
259 removeEntries(0); 259 removeEntries(0);
260 260
261#ifdef HAVE_FAM 261#ifdef HAVE_FAM
262 if (use_fam) { 262 if (use_fam) {
263 FAMClose(&fc); 263 FAMClose(&fc);
264 kdDebug(7001) << "KDirWatch deleted (FAM closed)" << endl; 264 kdDebug(7001) << "KDirWatch deleted (FAM closed)" << endl;
265 } 265 }
266#endif 266#endif
267 267
268} 268}
269 269
270#ifdef HAVE_DNOTIFY 270#ifdef HAVE_DNOTIFY
271void KDirWatchPrivate::slotActivated() 271void KDirWatchPrivate::slotActivated()
272{ 272{
273 char dummy_buf[100]; 273 char dummy_buf[100];
274 read(mPipe[0], &dummy_buf, 100); 274 read(mPipe[0], &dummy_buf, 100);
275 275
276 if (!mTimer.isActive()) 276 if (!mTimer.isActive())
277 mTimer.start(200, true); 277 mTimer.start(200, true);
278} 278}
279 279
280/* In DNOTIFY mode, only entries which are marked dirty are scanned. 280/* In DNOTIFY mode, only entries which are marked dirty are scanned.
281 * We first need to mark all yet nonexistant, but possible created 281 * We first need to mark all yet nonexistant, but possible created
282 * entries as dirty... 282 * entries as dirty...
283 */ 283 */
284void KDirWatchPrivate::Entry::propagate_dirty() 284void KDirWatchPrivate::Entry::propagate_dirty()
285{ 285{
286 Entry* sub_entry; 286 Entry* sub_entry;
287 for(sub_entry = m_entries.first(); sub_entry; sub_entry = m_entries.next()) 287 for(sub_entry = m_entries.first(); sub_entry; sub_entry = m_entries.next())
288 { 288 {
289 if (!sub_entry->dn_dirty) 289 if (!sub_entry->dn_dirty)
290 { 290 {
291 sub_entry->dn_dirty = true; 291 sub_entry->dn_dirty = true;
292 sub_entry->propagate_dirty(); 292 sub_entry->propagate_dirty();
293 } 293 }
294 } 294 }
295} 295}
296 296
297#else // !HAVE_DNOTIFY 297#else // !HAVE_DNOTIFY
298// slots always have to be defined... 298// slots always have to be defined...
299void KDirWatchPrivate::slotActivated() {} 299void KDirWatchPrivate::slotActivated() {}
300#endif 300#endif
301 301
302/* A KDirWatch instance is interested in getting events for 302/* A KDirWatch instance is interested in getting events for
303 * this file/Dir entry. 303 * this file/Dir entry.
304 */ 304 */
305void KDirWatchPrivate::Entry::addClient(KDirWatch* instance) 305void KDirWatchPrivate::Entry::addClient(KDirWatch* instance)
306{ 306{
307 Client* client = m_clients.first(); 307 Client* client = m_clients.first();
308 for(;client; client = m_clients.next()) 308 for(;client; client = m_clients.next())
309 if (client->instance == instance) break; 309 if (client->instance == instance) break;
310 310
311 if (client) { 311 if (client) {
312 client->count++; 312 client->count++;
313 return; 313 return;
314 } 314 }
315 315
316 client = new Client; 316 client = new Client;
317 client->instance = instance; 317 client->instance = instance;
318 client->count = 1; 318 client->count = 1;
319 client->watchingStopped = instance->isStopped(); 319 client->watchingStopped = instance->isStopped();
320 client->pending = NoChange; 320 client->pending = NoChange;
321 321
322 m_clients.append(client); 322 m_clients.append(client);
323} 323}
324 324
325void KDirWatchPrivate::Entry::removeClient(KDirWatch* instance) 325void KDirWatchPrivate::Entry::removeClient(KDirWatch* instance)
326{ 326{
327 Client* client = m_clients.first(); 327 Client* client = m_clients.first();
328 for(;client; client = m_clients.next()) 328 for(;client; client = m_clients.next())
329 if (client->instance == instance) break; 329 if (client->instance == instance) break;
330 330
331 if (client) { 331 if (client) {
332 client->count--; 332 client->count--;
333 if (client->count == 0) { 333 if (client->count == 0) {
334 m_clients.removeRef(client); 334 m_clients.removeRef(client);
335 delete client; 335 delete client;
336 } 336 }
337 } 337 }
338} 338}
339 339
340/* get number of clients */ 340/* get number of clients */
341int KDirWatchPrivate::Entry::clients() 341int KDirWatchPrivate::Entry::clients()
342{ 342{
343 int clients = 0; 343 int clients = 0;
344 Client* client = m_clients.first(); 344 Client* client = m_clients.first();
345 for(;client; client = m_clients.next()) 345 for(;client; client = m_clients.next())
346 clients += client->count; 346 clients += client->count;
347 347
348 return clients; 348 return clients;
349} 349}
350 350
351 351
352KDirWatchPrivate::Entry* KDirWatchPrivate::entry(const QString& _path) 352KDirWatchPrivate::Entry* KDirWatchPrivate::entry(const QString& _path)
353{ 353{
354// we only support absolute paths 354// we only support absolute paths
355 if (_path.left(1) != "/") { 355 if (_path.left(1) != "/") {
356 return 0; 356 return 0;
357 } 357 }
358 358
359 QString path = _path; 359 QString path = _path;
360 360
361 if ( path.length() > 1 && path.right(1) == "/" ) 361 if ( path.length() > 1 && path.right(1) == "/" )
362 path.truncate( path.length() - 1 ); 362 path.truncate( path.length() - 1 );
363 363
364 EntryMap::Iterator it = m_mapEntries.find( path ); 364 EntryMap::Iterator it = m_mapEntries.find( path );
365 if ( it == m_mapEntries.end() ) 365 if ( it == m_mapEntries.end() )
366 return 0; 366 return 0;
367 else 367 else
368 return &(*it); 368 return &(*it);
369} 369}
370 370
371// set polling frequency for a entry and adjust global freq if needed 371// set polling frequency for a entry and adjust global freq if needed
372void KDirWatchPrivate::useFreq(Entry* e, int newFreq) 372void KDirWatchPrivate::useFreq(Entry* e, int newFreq)
373{ 373{
374 e->freq = newFreq; 374 e->freq = newFreq;
375 375
376 // a reasonable frequency for the global polling timer 376 // a reasonable frequency for the global polling timer
377 if (e->freq < freq) { 377 if (e->freq < freq) {
378 freq = e->freq; 378 freq = e->freq;
379 if (timer->isActive()) timer->changeInterval(freq); 379 if (timer->isActive()) timer->changeInterval(freq);
380 kdDebug(7001) << "Global Poll Freq is now " << freq << " msec" << endl; 380 kdDebug(7001) << "Global Poll Freq is now " << freq << " msec" << endl;
381 } 381 }
382} 382}
383 383
384 384
385#if defined(HAVE_FAM) 385#if defined(HAVE_FAM)
386// setup FAM notification, returns false if not possible 386// setup FAM notification, returns false if not possible
387bool KDirWatchPrivate::useFAM(Entry* e) 387bool KDirWatchPrivate::useFAM(Entry* e)
388{ 388{
389 if (!use_fam) return false; 389 if (!use_fam) return false;
390 390
391 e->m_mode = FAMMode; 391 e->m_mode = FAMMode;
392 392
393 if (e->isDir) { 393 if (e->isDir) {
394 if (e->m_status == NonExistent) { 394 if (e->m_status == NonExistent) {
395 // If the directory does not exist we watch the parent directory 395 // If the directory does not exist we watch the parent directory
396 addEntry(0, QDir::cleanDirPath(e->path+"/.."), e, true); 396 addEntry(0, QDir::cleanDirPath(e->path+"/.."), e, true);
397 } 397 }
398 else { 398 else {
399 int res =FAMMonitorDirectory(&fc, QFile::encodeName(e->path), 399 int res =FAMMonitorDirectory(&fc, QFile::encodeName(e->path),
400 &(e->fr), e); 400 &(e->fr), e);
401 if (res<0) { 401 if (res<0) {
402 e->m_mode = UnknownMode; 402 e->m_mode = UnknownMode;
403 use_fam=false; 403 use_fam=false;
404 return false; 404 return false;
405 } 405 }
406 kdDebug(7001) << " Setup FAM (Req " 406 kdDebug(7001) << " Setup FAM (Req "
407 << FAMREQUEST_GETREQNUM(&(e->fr)) 407 << FAMREQUEST_GETREQNUM(&(e->fr))
408 << ") for " << e->path << endl; 408 << ") for " << e->path << endl;
409 } 409 }
410 } 410 }
411 else { 411 else {
412 if (e->m_status == NonExistent) { 412 if (e->m_status == NonExistent) {
413 // If the file does not exist we watch the directory 413 // If the file does not exist we watch the directory
414 addEntry(0, QFileInfo(e->path).dirPath(true), e, true); 414 addEntry(0, QFileInfo(e->path).dirPath(true), e, true);
415 } 415 }
416 else { 416 else {
417 int res = FAMMonitorFile(&fc, QFile::encodeName(e->path), 417 int res = FAMMonitorFile(&fc, QFile::encodeName(e->path),
418 &(e->fr), e); 418 &(e->fr), e);
419 if (res<0) { 419 if (res<0) {
420 e->m_mode = UnknownMode; 420 e->m_mode = UnknownMode;
421 use_fam=false; 421 use_fam=false;
422 return false; 422 return false;
423 } 423 }
424 424
425 kdDebug(7001) << " Setup FAM (Req " 425 kdDebug(7001) << " Setup FAM (Req "
426 << FAMREQUEST_GETREQNUM(&(e->fr)) 426 << FAMREQUEST_GETREQNUM(&(e->fr))
427 << ") for " << e->path << endl; 427 << ") for " << e->path << endl;
428 } 428 }
429 } 429 }
430 430
431 // handle FAM events to avoid deadlock 431 // handle FAM events to avoid deadlock
432 // (FAM sends back all files in a directory when monitoring) 432 // (FAM sends back all files in a directory when monitoring)
433 famEventReceived(); 433 famEventReceived();
434 434
435 return true; 435 return true;
436} 436}
437#endif 437#endif
438 438
439 439
440#ifdef HAVE_DNOTIFY 440#ifdef HAVE_DNOTIFY
441// setup DNotify notification, returns false if not possible 441// setup DNotify notification, returns false if not possible
442bool KDirWatchPrivate::useDNotify(Entry* e) 442bool KDirWatchPrivate::useDNotify(Entry* e)
443{ 443{
444 e->dn_fd = 0; 444 e->dn_fd = 0;
445 if (!supports_dnotify) return false; 445 if (!supports_dnotify) return false;
446 446
447 e->m_mode = DNotifyMode; 447 e->m_mode = DNotifyMode;
448 448
449 if (e->isDir) { 449 if (e->isDir) {
450 e->dn_dirty = false; 450 e->dn_dirty = false;
451 if (e->m_status == Normal) { 451 if (e->m_status == Normal) {
452 int fd = open(QFile::encodeName(e->path).data(), O_RDONLY); 452 int fd = open(QFile::encodeName(e->path).data(), O_RDONLY);
453 // Migrate fd to somewhere above 128. Some libraries have 453 // Migrate fd to somewhere above 128. Some libraries have
454 // constructs like: 454 // constructs like:
455 // fd = socket(...) 455 // fd = socket(...)
456 // if (fd > ARBITRARY_LIMIT) 456 // if (fd > ARBITRARY_LIMIT)
457 // return error; 457 // return error;
458 // 458 //
459 // Since programs might end up using a lot of KDirWatch objects 459 // Since programs might end up using a lot of KDirWatch objects
460 // for a rather long time the above braindamage could get 460 // for a rather long time the above braindamage could get
461 // triggered. 461 // triggered.
462 // 462 //
463 // By moving the kdirwatch fd's to > 128, calls like socket() will keep 463 // By moving the kdirwatch fd's to > 128, calls like socket() will keep
464 // returning fd's < ARBITRARY_LIMIT for a bit longer. 464 // returning fd's < ARBITRARY_LIMIT for a bit longer.
465 int fd2 = fcntl(fd, F_DUPFD, 128); 465 int fd2 = fcntl(fd, F_DUPFD, 128);
466 if (fd2 >= 0) 466 if (fd2 >= 0)
467 { 467 {
468 close(fd); 468 close(fd);
469 fd = fd2; 469 fd = fd2;
470 } 470 }
471 if (fd<0) { 471 if (fd<0) {
472 e->m_mode = UnknownMode; 472 e->m_mode = UnknownMode;
473 return false; 473 return false;
474 } 474 }
475 475
476 int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT; 476 int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
477 // if dependant is a file watch, we check for MODIFY & ATTRIB too 477 // if dependant is a file watch, we check for MODIFY & ATTRIB too
478 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next()) 478 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
479 if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB; break; } 479 if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB; break; }
480 480
481 if(fcntl(fd, F_SETSIG, dnotify_signal) < 0 || 481 if(fcntl(fd, F_SETSIG, dnotify_signal) < 0 ||
482 fcntl(fd, F_NOTIFY, mask) < 0) { 482 fcntl(fd, F_NOTIFY, mask) < 0) {
483 483
484 kdDebug(7001) << "Not using Linux Directory Notifications." 484 kdDebug(7001) << "Not using Linux Directory Notifications."
485 << endl; 485 << endl;
486 supports_dnotify = false; 486 supports_dnotify = false;
487 ::close(fd); 487 ::close(fd);
488 e->m_mode = UnknownMode; 488 e->m_mode = UnknownMode;
489 return false; 489 return false;
490 } 490 }
491 491
492 fd_Entry.replace(fd, e); 492 fd_Entry.replace(fd, e);
493 e->dn_fd = fd; 493 e->dn_fd = fd;
494 494
495 kdDebug(7001) << " Setup DNotify (fd " << fd 495 kdDebug(7001) << " Setup DNotify (fd " << fd
496 << ") for " << e->path << endl; 496 << ") for " << e->path << endl;
497 } 497 }
498 else { // NotExisting 498 else { // NotExisting
499 addEntry(0, QDir::cleanDirPath(e->path+"/.."), e, true); 499 addEntry(0, QDir::cleanDirPath(e->path+"/.."), e, true);
500 } 500 }
501 } 501 }
502 else { // File 502 else { // File
503 // we always watch the directory (DNOTIFY can't watch files alone) 503 // we always watch the directory (DNOTIFY can't watch files alone)
504 // this notifies us about changes of files therein 504 // this notifies us about changes of files therein
505 addEntry(0, QFileInfo(e->path).dirPath(true), e, true); 505 addEntry(0, QFileInfo(e->path).dirPath(true), e, true);
506 } 506 }
507 507
508 return true; 508 return true;
509} 509}
510#endif 510#endif
511 511
512 512
513bool KDirWatchPrivate::useStat(Entry* e) 513bool KDirWatchPrivate::useStat(Entry* e)
514{ 514{
515//US we have no KIO::probably_slow_mounted. So disable this part 515//US we have no KIO::probably_slow_mounted. So disable this part
516//US if (KIO::probably_slow_mounted(e->path)) 516//US if (KIO::probably_slow_mounted(e->path))
517//US useFreq(e, m_nfsPollInterval); 517//US useFreq(e, m_nfsPollInterval);
518//US else 518//US else
519 useFreq(e, m_PollInterval); 519 useFreq(e, m_PollInterval);
520 520
521 if (e->m_mode != StatMode) { 521 if (e->m_mode != StatMode) {
522 e->m_mode = StatMode; 522 e->m_mode = StatMode;
523 statEntries++; 523 statEntries++;
524 524
525 if ( statEntries == 1 ) { 525 if ( statEntries == 1 ) {
526 // if this was first STAT entry (=timer was stopped) 526 // if this was first STAT entry (=timer was stopped)
527 timer->start(freq); // then start the timer 527 timer->start(freq); // then start the timer
528 kdDebug(7001) << " Started Polling Timer, freq " << freq << endl; 528 kdDebug(7001) << " Started Polling Timer, freq " << freq << endl;
529 } 529 }
530 } 530 }
531 531
532 kdDebug(7001) << " Setup Stat (freq " << e->freq 532 kdDebug(7001) << " Setup Stat (freq " << e->freq
533 << ") for " << e->path << endl; 533 << ") for " << e->path << endl;
534 534
535 return true; 535 return true;
536} 536}
537 537
538 538
539/* If <instance> !=0, this KDirWatch instance wants to watch at <_path>, 539/* If <instance> !=0, this KDirWatch instance wants to watch at <_path>,
540 * providing in <isDir> the type of the entry to be watched. 540 * providing in <isDir> the type of the entry to be watched.
541 * Sometimes, entries are dependant on each other: if <sub_entry> !=0, 541 * Sometimes, entries are dependant on each other: if <sub_entry> !=0,
542 * this entry needs another entry to watch himself (when notExistent). 542 * this entry needs another entry to watch himself (when notExistent).
543 */ 543 */
544void KDirWatchPrivate::addEntry(KDirWatch* instance, const QString& _path, 544void KDirWatchPrivate::addEntry(KDirWatch* instance, const QString& _path,
545 Entry* sub_entry, bool isDir) 545 Entry* sub_entry, bool isDir)
546{ 546{
547 QString path = _path; 547 QString path = _path;
548 if (path.startsWith("/dev/") || (path == "/dev")) 548 if (path.startsWith("/dev/") || (path == "/dev"))
549 return; // Don't even go there. 549 return; // Don't even go there.
550 550
551 if ( path.length() > 1 && path.right(1) == "/" ) 551 if ( path.length() > 1 && path.right(1) == "/" )
552 path.truncate( path.length() - 1 ); 552 path.truncate( path.length() - 1 );
553 553
554 EntryMap::Iterator it = m_mapEntries.find( path ); 554 EntryMap::Iterator it = m_mapEntries.find( path );
555 if ( it != m_mapEntries.end() ) 555 if ( it != m_mapEntries.end() )
556 { 556 {
557 if (sub_entry) { 557 if (sub_entry) {
558 (*it).m_entries.append(sub_entry); 558 (*it).m_entries.append(sub_entry);
559 kdDebug(7001) << "Added already watched Entry " << path 559 kdDebug(7001) << "Added already watched Entry " << path
560 << " (for " << sub_entry->path << ")" << endl; 560 << " (for " << sub_entry->path << ")" << endl;
561#ifdef HAVE_DNOTIFY 561#ifdef HAVE_DNOTIFY
562 Entry* e = &(*it); 562 Entry* e = &(*it);
563 if( e->dn_fd > 0 ) { 563 if( e->dn_fd > 0 ) {
564 int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT; 564 int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
565 // if dependant is a file watch, we check for MODIFY & ATTRIB too 565 // if dependant is a file watch, we check for MODIFY & ATTRIB too
566 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next()) 566 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
567 if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB; break; } 567 if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB; break; }
568 if( fcntl(e->dn_fd, F_NOTIFY, mask) < 0) { // shouldn't happen 568 if( fcntl(e->dn_fd, F_NOTIFY, mask) < 0) { // shouldn't happen
569 ::close(e->dn_fd); 569 ::close(e->dn_fd);
570 e->m_mode = UnknownMode; 570 e->m_mode = UnknownMode;
571 fd_Entry.remove(e->dn_fd); 571 fd_Entry.remove(e->dn_fd);
572 e->dn_fd = 0; 572 e->dn_fd = 0;
573 useStat( e ); 573 useStat( e );
574 } 574 }
575 } 575 }
576#endif 576#endif
577 } 577 }
578 else { 578 else {
579 (*it).addClient(instance); 579 (*it).addClient(instance);
580 kdDebug(7001) << "Added already watched Entry " << path 580 kdDebug(7001) << "Added already watched Entry " << path
581 << " (now " << (*it).clients() << " clients)" 581 << " (now " << (*it).clients() << " clients)"
582 << QString(" [%1]").arg(instance->name()) << endl; 582 << QString(" [%1]").arg(instance->name()) << endl;
583 } 583 }
584 return; 584 return;
585 } 585 }
586 586
587 // we have a new path to watch 587 // we have a new path to watch
588 588
589 struct stat stat_buf; 589 struct stat stat_buf;
590 bool exists = (stat(QFile::encodeName(path), &stat_buf) == 0); 590 bool exists = (stat(QFile::encodeName(path), &stat_buf) == 0);
591 591
592 Entry newEntry; 592 Entry newEntry;
593 m_mapEntries.insert( path, newEntry ); 593 m_mapEntries.insert( path, newEntry );
594 // the insert does a copy, so we have to use <e> now 594 // the insert does a copy, so we have to use <e> now
595 Entry* e = &(m_mapEntries[path]); 595 Entry* e = &(m_mapEntries[path]);
596 596
597 if (exists) { 597 if (exists) {
598 e->isDir = S_ISDIR(stat_buf.st_mode); 598 QFileInfo fi ( path );
599 e->isDir = fi.isDir();
599 600
600 if (e->isDir && !isDir) 601 if (e->isDir && !isDir)
601 qWarning("KDirWatch: %s is a directory. Use addDir!", path.ascii()); 602 qWarning("KDirWatch: %s is a directory. Use addDir!", path.ascii());
602 else if (!e->isDir && isDir) 603 else if (!e->isDir && isDir)
603 qWarning("KDirWatch: %s is a file. Use addFile!", path.ascii()); 604 qWarning("KDirWatch: %s is a file. Use addFile!", path.ascii());
604 605
605 e->m_ctime = stat_buf.st_ctime; 606 e->m_ctime = stat_buf.st_ctime;
606 e->m_status = Normal; 607 e->m_status = Normal;
607 e->m_nlink = stat_buf.st_nlink; 608 e->m_nlink = stat_buf.st_nlink;
608 } 609 }
609 else { 610 else {
610 e->isDir = isDir; 611 e->isDir = isDir;
611 e->m_ctime = invalid_ctime; 612 e->m_ctime = invalid_ctime;
612 e->m_status = NonExistent; 613 e->m_status = NonExistent;
613 e->m_nlink = 0; 614 e->m_nlink = 0;
614 } 615 }
615 616
616 e->path = path; 617 e->path = path;
617 if (sub_entry) 618 if (sub_entry)
618 e->m_entries.append(sub_entry); 619 e->m_entries.append(sub_entry);
619 else 620 else
620 e->addClient(instance); 621 e->addClient(instance);
621 622
622 kdDebug(7001) << "Added " << (e->isDir ? "Dir ":"File ") << path 623 kdDebug(7001) << "Added " << (e->isDir ? "Dir ":"File ") << path
623 << (e->m_status == NonExistent ? " NotExisting" : "") 624 << (e->m_status == NonExistent ? " NotExisting" : "")
624 << (sub_entry ? QString(" for %1").arg(sub_entry->path) : QString("")) 625 << (sub_entry ? QString(" for %1").arg(sub_entry->path) : QString(""))
625 << (instance ? QString(" [%1]").arg(instance->name()) : QString("")) 626 << (instance ? QString(" [%1]").arg(instance->name()) : QString(""))
626 << endl; 627 << endl;
627 628
628 629
629 // now setup the notification method 630 // now setup the notification method
630 e->m_mode = UnknownMode; 631 e->m_mode = UnknownMode;
631 e->msecLeft = 0; 632 e->msecLeft = 0;
632 633
633#if defined(HAVE_FAM) 634#if defined(HAVE_FAM)
634 if (useFAM(e)) return; 635 if (useFAM(e)) return;
635#endif 636#endif
636 637
637#ifdef HAVE_DNOTIFY 638#ifdef HAVE_DNOTIFY
638 if (useDNotify(e)) return; 639 if (useDNotify(e)) return;
639#endif 640#endif
640 641
641 useStat(e); 642 useStat(e);
642} 643}
643 644
644 645
645void KDirWatchPrivate::removeEntry( KDirWatch* instance, 646void KDirWatchPrivate::removeEntry( KDirWatch* instance,
646 const QString& _path, Entry* sub_entry ) 647 const QString& _path, Entry* sub_entry )
647{ 648{
648 Entry* e = entry(_path); 649 Entry* e = entry(_path);
649 if (!e) { 650 if (!e) {
650 kdWarning(7001) << "KDirWatch::removeDir can't handle '" << _path << "'" << endl; 651 kdWarning(7001) << "KDirWatch::removeDir can't handle '" << _path << "'" << endl;
651 return; 652 return;
652 } 653 }
653 654
654 if (sub_entry) 655 if (sub_entry)
655 e->m_entries.removeRef(sub_entry); 656 e->m_entries.removeRef(sub_entry);
656 else 657 else
657 e->removeClient(instance); 658 e->removeClient(instance);
658 659
659 if (e->m_clients.count() || e->m_entries.count()) 660 if (e->m_clients.count() || e->m_entries.count())
660 return; 661 return;
661 662
662 if (delayRemove) { 663 if (delayRemove) {
663 // removeList is allowed to contain any entry at most once 664 // removeList is allowed to contain any entry at most once
664 if (removeList.findRef(e)==-1) 665 if (removeList.findRef(e)==-1)
665 removeList.append(e); 666 removeList.append(e);
666 // now e->isValid() is false 667 // now e->isValid() is false
667 return; 668 return;
668 } 669 }
669 670
670#ifdef HAVE_FAM 671#ifdef HAVE_FAM
671 if (e->m_mode == FAMMode) { 672 if (e->m_mode == FAMMode) {
672 if ( e->m_status == Normal) { 673 if ( e->m_status == Normal) {
673 FAMCancelMonitor(&fc, &(e->fr) ); 674 FAMCancelMonitor(&fc, &(e->fr) );
674 kdDebug(7001) << "Cancelled FAM (Req " 675 kdDebug(7001) << "Cancelled FAM (Req "
675 << FAMREQUEST_GETREQNUM(&(e->fr)) 676 << FAMREQUEST_GETREQNUM(&(e->fr))
676 << ") for " << e->path << endl; 677 << ") for " << e->path << endl;
677 } 678 }
678 else { 679 else {
679 if (e->isDir) 680 if (e->isDir)
680 removeEntry(0, QDir::cleanDirPath(e->path+"/.."), e); 681 removeEntry(0, QDir::cleanDirPath(e->path+"/.."), e);
681 else 682 else
682 removeEntry(0, QFileInfo(e->path).dirPath(true), e); 683 removeEntry(0, QFileInfo(e->path).dirPath(true), e);
683 } 684 }
684 } 685 }
685#endif 686#endif
686 687
687#ifdef HAVE_DNOTIFY 688#ifdef HAVE_DNOTIFY
688 if (e->m_mode == DNotifyMode) { 689 if (e->m_mode == DNotifyMode) {
689 if (!e->isDir) { 690 if (!e->isDir) {
690 removeEntry(0, QFileInfo(e->path).dirPath(true), e); 691 removeEntry(0, QFileInfo(e->path).dirPath(true), e);
691 } 692 }
692 else { // isDir 693 else { // isDir
693 // must close the FD. 694 // must close the FD.
694 if ( e->m_status == Normal) { 695 if ( e->m_status == Normal) {
695 if (e->dn_fd) { 696 if (e->dn_fd) {
696 ::close(e->dn_fd); 697 ::close(e->dn_fd);
697 fd_Entry.remove(e->dn_fd); 698 fd_Entry.remove(e->dn_fd);
698 699
699 kdDebug(7001) << "Cancelled DNotify (fd " << e->dn_fd 700 kdDebug(7001) << "Cancelled DNotify (fd " << e->dn_fd
700 << ") for " << e->path << endl; 701 << ") for " << e->path << endl;
701 e->dn_fd = 0; 702 e->dn_fd = 0;
702 703
703 } 704 }
704 } 705 }
705 else { 706 else {
706 removeEntry(0, QDir::cleanDirPath(e->path+"/.."), e); 707 removeEntry(0, QDir::cleanDirPath(e->path+"/.."), e);
707 } 708 }
708 } 709 }
709 } 710 }
710#endif 711#endif
711 712
712 if (e->m_mode == StatMode) { 713 if (e->m_mode == StatMode) {
713 statEntries--; 714 statEntries--;
714 if ( statEntries == 0 ) { 715 if ( statEntries == 0 ) {
715 timer->stop(); // stop timer if lists are empty 716 timer->stop(); // stop timer if lists are empty
716 kdDebug(7001) << " Stopped Polling Timer" << endl; 717 kdDebug(7001) << " Stopped Polling Timer" << endl;
717 } 718 }
718 } 719 }
719 720
720 kdDebug(7001) << "Removed " << (e->isDir ? "Dir ":"File ") << e->path 721 kdDebug(7001) << "Removed " << (e->isDir ? "Dir ":"File ") << e->path
721 << (sub_entry ? QString(" for %1").arg(sub_entry->path) : QString("")) 722 << (sub_entry ? QString(" for %1").arg(sub_entry->path) : QString(""))
722 << (instance ? QString(" [%1]").arg(instance->name()) : QString("")) 723 << (instance ? QString(" [%1]").arg(instance->name()) : QString(""))
723 << endl; 724 << endl;
724 m_mapEntries.remove( e->path ); // <e> not valid any more 725 m_mapEntries.remove( e->path ); // <e> not valid any more
725} 726}
726 727
727 728
728/* Called from KDirWatch destructor: 729/* Called from KDirWatch destructor:
729 * remove <instance> as client from all entries 730 * remove <instance> as client from all entries
730 */ 731 */
731void KDirWatchPrivate::removeEntries( KDirWatch* instance ) 732void KDirWatchPrivate::removeEntries( KDirWatch* instance )
732{ 733{
733 QPtrList<Entry> list; 734 QPtrList<Entry> list;
734 int minfreq = 3600000; 735 int minfreq = 3600000;
735 736
736 // put all entries where instance is a client in list 737 // put all entries where instance is a client in list
737 EntryMap::Iterator it = m_mapEntries.begin(); 738 EntryMap::Iterator it = m_mapEntries.begin();
738 for( ; it != m_mapEntries.end(); ++it ) { 739 for( ; it != m_mapEntries.end(); ++it ) {
739 Client* c = (*it).m_clients.first(); 740 Client* c = (*it).m_clients.first();
740 for(;c;c=(*it).m_clients.next()) 741 for(;c;c=(*it).m_clients.next())
741 if (c->instance == instance) break; 742 if (c->instance == instance) break;
742 if (c) { 743 if (c) {
743 c->count = 1; // forces deletion of instance as client 744 c->count = 1; // forces deletion of instance as client
744 list.append(&(*it)); 745 list.append(&(*it));
745 } 746 }
746 else if ( (*it).m_mode == StatMode && (*it).freq < minfreq ) 747 else if ( (*it).m_mode == StatMode && (*it).freq < minfreq )
747 minfreq = (*it).freq; 748 minfreq = (*it).freq;
748 } 749 }
749 750
750 for(Entry* e=list.first();e;e=list.next()) 751 for(Entry* e=list.first();e;e=list.next())
751 removeEntry(instance, e->path, 0); 752 removeEntry(instance, e->path, 0);
752 753
753 if (minfreq > freq) { 754 if (minfreq > freq) {
754 // we can decrease the global polling frequency 755 // we can decrease the global polling frequency
755 freq = minfreq; 756 freq = minfreq;
756 if (timer->isActive()) timer->changeInterval(freq); 757 if (timer->isActive()) timer->changeInterval(freq);
757 kdDebug(7001) << "Poll Freq now " << freq << " msec" << endl; 758 kdDebug(7001) << "Poll Freq now " << freq << " msec" << endl;
758 } 759 }
759} 760}
760 761
761// instance ==0: stop scanning for all instances 762// instance ==0: stop scanning for all instances
762bool KDirWatchPrivate::stopEntryScan( KDirWatch* instance, Entry* e) 763bool KDirWatchPrivate::stopEntryScan( KDirWatch* instance, Entry* e)
763{ 764{
764 int stillWatching = 0; 765 int stillWatching = 0;
765 Client* c = e->m_clients.first(); 766 Client* c = e->m_clients.first();
766 for(;c;c=e->m_clients.next()) { 767 for(;c;c=e->m_clients.next()) {
767 if (!instance || instance == c->instance) 768 if (!instance || instance == c->instance)
768 c->watchingStopped = true; 769 c->watchingStopped = true;
769 else if (!c->watchingStopped) 770 else if (!c->watchingStopped)
770 stillWatching += c->count; 771 stillWatching += c->count;
771 } 772 }
772 773
773 kdDebug(7001) << instance->name() << " stopped scanning " << e->path 774 kdDebug(7001) << instance->name() << " stopped scanning " << e->path
774 << " (now " << stillWatching << " watchers)" << endl; 775 << " (now " << stillWatching << " watchers)" << endl;
775 776
776 if (stillWatching == 0) { 777 if (stillWatching == 0) {
777 // if nobody is interested, we don't watch 778 // if nobody is interested, we don't watch
778 e->m_ctime = invalid_ctime; // invalid 779 e->m_ctime = invalid_ctime; // invalid
779 // e->m_status = Normal; 780 // e->m_status = Normal;
780 } 781 }
781 return true; 782 return true;
782} 783}
783 784
784// instance ==0: start scanning for all instances 785// instance ==0: start scanning for all instances
785bool KDirWatchPrivate::restartEntryScan( KDirWatch* instance, Entry* e, 786bool KDirWatchPrivate::restartEntryScan( KDirWatch* instance, Entry* e,
786 bool notify) 787 bool notify)
787{ 788{
788 int wasWatching = 0, newWatching = 0; 789 int wasWatching = 0, newWatching = 0;
789 Client* c = e->m_clients.first(); 790 Client* c = e->m_clients.first();
790 for(;c;c=e->m_clients.next()) { 791 for(;c;c=e->m_clients.next()) {
791 if (!c->watchingStopped) 792 if (!c->watchingStopped)
792 wasWatching += c->count; 793 wasWatching += c->count;
793 else if (!instance || instance == c->instance) { 794 else if (!instance || instance == c->instance) {
794 c->watchingStopped = false; 795 c->watchingStopped = false;
795 newWatching += c->count; 796 newWatching += c->count;
796 } 797 }
797 } 798 }
798 if (newWatching == 0) 799 if (newWatching == 0)
799 return false; 800 return false;
800 801
801 kdDebug(7001) << instance->name() << " restarted scanning " << e->path 802 kdDebug(7001) << instance->name() << " restarted scanning " << e->path
802 << " (now " << wasWatching+newWatching << " watchers)" << endl; 803 << " (now " << wasWatching+newWatching << " watchers)" << endl;
803 804
804 // restart watching and emit pending events 805 // restart watching and emit pending events
805 806
806 int ev = NoChange; 807 int ev = NoChange;
807 if (wasWatching == 0) { 808 if (wasWatching == 0) {
808 if (!notify) { 809 if (!notify) {
809 struct stat stat_buf; 810 struct stat stat_buf;
810 bool exists = (stat(QFile::encodeName(e->path), &stat_buf) == 0); 811 bool exists = (stat(QFile::encodeName(e->path), &stat_buf) == 0);
811 if (exists) { 812 if (exists) {
812 e->m_ctime = stat_buf.st_ctime; 813 e->m_ctime = stat_buf.st_ctime;
813 e->m_status = Normal; 814 e->m_status = Normal;
814 e->m_nlink = stat_buf.st_nlink; 815 e->m_nlink = stat_buf.st_nlink;
815 } 816 }
816 else { 817 else {
817 e->m_ctime = invalid_ctime; 818 e->m_ctime = invalid_ctime;
818 e->m_status = NonExistent; 819 e->m_status = NonExistent;
819 e->m_nlink = 0; 820 e->m_nlink = 0;
820 } 821 }
821 } 822 }
822 e->msecLeft = 0; 823 e->msecLeft = 0;
823 ev = scanEntry(e); 824 ev = scanEntry(e);
824 } 825 }
825 emitEvent(e,ev); 826 emitEvent(e,ev);
826 827
827 return true; 828 return true;
828} 829}
829 830
830// instance ==0: stop scanning for all instances 831// instance ==0: stop scanning for all instances
831void KDirWatchPrivate::stopScan(KDirWatch* instance) 832void KDirWatchPrivate::stopScan(KDirWatch* instance)
832{ 833{
833 EntryMap::Iterator it = m_mapEntries.begin(); 834 EntryMap::Iterator it = m_mapEntries.begin();
834 for( ; it != m_mapEntries.end(); ++it ) 835 for( ; it != m_mapEntries.end(); ++it )
835 stopEntryScan(instance, &(*it)); 836 stopEntryScan(instance, &(*it));
836} 837}
837 838
838 839
839void KDirWatchPrivate::startScan(KDirWatch* instance, 840void KDirWatchPrivate::startScan(KDirWatch* instance,
840 bool notify, bool skippedToo ) 841 bool notify, bool skippedToo )
841{ 842{
842 if (!notify) 843 if (!notify)
843 resetList(instance,skippedToo); 844 resetList(instance,skippedToo);
844 845
845 EntryMap::Iterator it = m_mapEntries.begin(); 846 EntryMap::Iterator it = m_mapEntries.begin();
846 for( ; it != m_mapEntries.end(); ++it ) 847 for( ; it != m_mapEntries.end(); ++it )
847 restartEntryScan(instance, &(*it), notify); 848 restartEntryScan(instance, &(*it), notify);
848 849
849 // timer should still be running when in polling mode 850 // timer should still be running when in polling mode
850} 851}
851 852
852 853
853// clear all pending events, also from stopped 854// clear all pending events, also from stopped
854void KDirWatchPrivate::resetList( KDirWatch* /*instance*/, 855void KDirWatchPrivate::resetList( KDirWatch* /*instance*/,
855 bool skippedToo ) 856 bool skippedToo )
856{ 857{
857 EntryMap::Iterator it = m_mapEntries.begin(); 858 EntryMap::Iterator it = m_mapEntries.begin();
858 for( ; it != m_mapEntries.end(); ++it ) { 859 for( ; it != m_mapEntries.end(); ++it ) {
859 860
860 Client* c = (*it).m_clients.first(); 861 Client* c = (*it).m_clients.first();
861 for(;c;c=(*it).m_clients.next()) 862 for(;c;c=(*it).m_clients.next())
862 if (!c->watchingStopped || skippedToo) 863 if (!c->watchingStopped || skippedToo)
863 c->pending = NoChange; 864 c->pending = NoChange;
864 } 865 }
865} 866}
866 867
867// Return event happened on <e> 868// Return event happened on <e>
868// 869//
869int KDirWatchPrivate::scanEntry(Entry* e) 870int KDirWatchPrivate::scanEntry(Entry* e)
870{ 871{
871#ifdef HAVE_FAM 872#ifdef HAVE_FAM
872 // we do not stat entries using FAM 873 // we do not stat entries using FAM
873 if (e->m_mode == FAMMode) return NoChange; 874 if (e->m_mode == FAMMode) return NoChange;
874#endif 875#endif
875 876
876 // Shouldn't happen: Ignore "unknown" notification method 877 // Shouldn't happen: Ignore "unknown" notification method
877 if (e->m_mode == UnknownMode) return NoChange; 878 if (e->m_mode == UnknownMode) return NoChange;
878 879
879#ifdef HAVE_DNOTIFY 880#ifdef HAVE_DNOTIFY
880 if (e->m_mode == DNotifyMode) { 881 if (e->m_mode == DNotifyMode) {
881 // we know nothing has changed, no need to stat 882 // we know nothing has changed, no need to stat
882 if(!e->dn_dirty) return NoChange; 883 if(!e->dn_dirty) return NoChange;
883 e->dn_dirty = false; 884 e->dn_dirty = false;
884 } 885 }
885#endif 886#endif
886 887
887 if (e->m_mode == StatMode) { 888 if (e->m_mode == StatMode) {
888 // only scan if timeout on entry timer happens; 889 // only scan if timeout on entry timer happens;
889 // e.g. when using 500msec global timer, a entry 890 // e.g. when using 500msec global timer, a entry
890 // with freq=5000 is only watched every 10th time 891 // with freq=5000 is only watched every 10th time
891 892
892 e->msecLeft -= freq; 893 e->msecLeft -= freq;
893 if (e->msecLeft>0) return NoChange; 894 if (e->msecLeft>0) return NoChange;
894 e->msecLeft += e->freq; 895 e->msecLeft += e->freq;
895 } 896 }
896 897
897 struct stat stat_buf; 898 struct stat stat_buf;
898 bool exists = (stat(QFile::encodeName(e->path), &stat_buf) == 0); 899 bool exists = (stat(QFile::encodeName(e->path), &stat_buf) == 0);
899 if (exists) { 900 if (exists) {
900 901
901 if (e->m_status == NonExistent) { 902 if (e->m_status == NonExistent) {
902 e->m_ctime = stat_buf.st_ctime; 903 e->m_ctime = stat_buf.st_ctime;
903 e->m_status = Normal; 904 e->m_status = Normal;
904 e->m_nlink = stat_buf.st_nlink; 905 e->m_nlink = stat_buf.st_nlink;
905 return Created; 906 return Created;
906 } 907 }
907 908
908 if ( (e->m_ctime != invalid_ctime) && 909 if ( (e->m_ctime != invalid_ctime) &&
909 ((stat_buf.st_ctime != e->m_ctime) || 910 ((stat_buf.st_ctime != e->m_ctime) ||
910 // (stat_buf.st_nlink != (nlink_t) e->m_nlink)) ) { 911 // (stat_buf.st_nlink != (nlink_t) e->m_nlink)) ) {
911 (stat_buf.st_nlink != e->m_nlink)) ) { 912 (stat_buf.st_nlink != e->m_nlink)) ) {
912 e->m_ctime = stat_buf.st_ctime; 913 e->m_ctime = stat_buf.st_ctime;
913 e->m_nlink = stat_buf.st_nlink; 914 e->m_nlink = stat_buf.st_nlink;
914 return Changed; 915 return Changed;
915 } 916 }
916 917
917 return NoChange; 918 return NoChange;
918 } 919 }
919 920
920 // dir/file doesn't exist 921 // dir/file doesn't exist
921 922
922 if (e->m_ctime == invalid_ctime) 923 if (e->m_ctime == invalid_ctime)
923 return NoChange; 924 return NoChange;
924 925
925 e->m_ctime = invalid_ctime; 926 e->m_ctime = invalid_ctime;
926 e->m_nlink = 0; 927 e->m_nlink = 0;
927 e->m_status = NonExistent; 928 e->m_status = NonExistent;
928 929
929 return Deleted; 930 return Deleted;
930} 931}
931 932
932/* Notify all interested KDirWatch instances about a given event on an entry 933/* Notify all interested KDirWatch instances about a given event on an entry
933 * and stored pending events. When watching is stopped, the event is 934 * and stored pending events. When watching is stopped, the event is
934 * added to the pending events. 935 * added to the pending events.
935 */ 936 */
936void KDirWatchPrivate::emitEvent(Entry* e, int event, const QString &fileName) 937void KDirWatchPrivate::emitEvent(Entry* e, int event, const QString &fileName)
937{ 938{
938 QString path = e->path; 939 QString path = e->path;
939 if (!fileName.isEmpty()) { 940 if (!fileName.isEmpty()) {
940 if (fileName[0] == '/') 941 if (fileName[0] == '/')
941 path = fileName; 942 path = fileName;
942 else 943 else
943 path += "/" + fileName; 944 path += "/" + fileName;
944 } 945 }
945 946
946 Client* c = e->m_clients.first(); 947 Client* c = e->m_clients.first();
947 for(;c;c=e->m_clients.next()) { 948 for(;c;c=e->m_clients.next()) {
948 if (c->instance==0 || c->count==0) continue; 949 if (c->instance==0 || c->count==0) continue;
949 950
950 if (c->watchingStopped) { 951 if (c->watchingStopped) {
951 // add event to pending... 952 // add event to pending...
952 if (event == Changed) 953 if (event == Changed)
953 c->pending |= event; 954 c->pending |= event;
954 else if (event == Created || event == Deleted) 955 else if (event == Created || event == Deleted)
955 c->pending = event; 956 c->pending = event;
956 continue; 957 continue;
957 } 958 }
958 // not stopped 959 // not stopped
959 if (event == NoChange || event == Changed) 960 if (event == NoChange || event == Changed)
960 event |= c->pending; 961 event |= c->pending;
961 c->pending = NoChange; 962 c->pending = NoChange;
962 if (event == NoChange) continue; 963 if (event == NoChange) continue;
963 964
964 if (event & Deleted) { 965 if (event & Deleted) {
965 c->instance->setDeleted(path); 966 c->instance->setDeleted(path);
966 // emit only Deleted event... 967 // emit only Deleted event...
967 continue; 968 continue;
968 } 969 }
969 970
970 if (event & Created) { 971 if (event & Created) {
971 c->instance->setCreated(path); 972 c->instance->setCreated(path);
972 // possible emit Change event after creation 973 // possible emit Change event after creation
973 } 974 }
974 975
975 if (event & Changed) 976 if (event & Changed)
976 c->instance->setDirty(path); 977 c->instance->setDirty(path);
977 } 978 }
978} 979}
979 980
980// Remove entries which were marked to be removed 981// Remove entries which were marked to be removed
981void KDirWatchPrivate::slotRemoveDelayed() 982void KDirWatchPrivate::slotRemoveDelayed()
982{ 983{
983 Entry* e; 984 Entry* e;
984 delayRemove = false; 985 delayRemove = false;
985 for(e=removeList.first();e;e=removeList.next()) 986 for(e=removeList.first();e;e=removeList.next())
986 removeEntry(0, e->path, 0); 987 removeEntry(0, e->path, 0);
987 removeList.clear(); 988 removeList.clear();
988} 989}
989 990
990/* Scan all entries to be watched for changes. This is done regularly 991/* Scan all entries to be watched for changes. This is done regularly
991 * when polling and once after a DNOTIFY signal. This is NOT used by FAM. 992 * when polling and once after a DNOTIFY signal. This is NOT used by FAM.
992 */ 993 */
993void KDirWatchPrivate::slotRescan() 994void KDirWatchPrivate::slotRescan()
994{ 995{
995 EntryMap::Iterator it; 996 EntryMap::Iterator it;
996 997
997 // People can do very long things in the slot connected to dirty(), 998 // People can do very long things in the slot connected to dirty(),
998 // like showing a message box. We don't want to keep polling during 999 // like showing a message box. We don't want to keep polling during
999 // that time, otherwise the value of 'delayRemove' will be reset. 1000 // that time, otherwise the value of 'delayRemove' will be reset.
1000 bool timerRunning = timer->isActive(); 1001 bool timerRunning = timer->isActive();
1001 if ( timerRunning ) 1002 if ( timerRunning )
1002 timer->stop(); 1003 timer->stop();
1003 1004
1004 // We delay deletions of entries this way. 1005 // We delay deletions of entries this way.
1005 // removeDir(), when called in slotDirty(), can cause a crash otherwise 1006 // removeDir(), when called in slotDirty(), can cause a crash otherwise
1006 delayRemove = true; 1007 delayRemove = true;
1007 1008
1008#ifdef HAVE_DNOTIFY 1009#ifdef HAVE_DNOTIFY
1009 QPtrList<Entry> dList, cList; 1010 QPtrList<Entry> dList, cList;
1010 1011
1011 // for DNotify method, 1012 // for DNotify method,
1012 if (rescan_all) 1013 if (rescan_all)
1013 { 1014 {
1014 // mark all as dirty 1015 // mark all as dirty
1015 it = m_mapEntries.begin(); 1016 it = m_mapEntries.begin();
1016 for( ; it != m_mapEntries.end(); ++it ) 1017 for( ; it != m_mapEntries.end(); ++it )
1017 (*it).dn_dirty = true; 1018 (*it).dn_dirty = true;
1018 rescan_all = false; 1019 rescan_all = false;
1019 } 1020 }
1020 else 1021 else
1021 { 1022 {
1022 // progate dirty flag to dependant entries (e.g. file watches) 1023 // progate dirty flag to dependant entries (e.g. file watches)
1023 it = m_mapEntries.begin(); 1024 it = m_mapEntries.begin();
1024 for( ; it != m_mapEntries.end(); ++it ) 1025 for( ; it != m_mapEntries.end(); ++it )
1025 if ( ((*it).m_mode == DNotifyMode) && (*it).dn_dirty ) 1026 if ( ((*it).m_mode == DNotifyMode) && (*it).dn_dirty )
1026 (*it).propagate_dirty(); 1027 (*it).propagate_dirty();
1027 } 1028 }
1028 1029
1029#endif 1030#endif
1030 1031
1031 it = m_mapEntries.begin(); 1032 it = m_mapEntries.begin();
1032 for( ; it != m_mapEntries.end(); ++it ) { 1033 for( ; it != m_mapEntries.end(); ++it ) {
1033 // we don't check invalid entries (i.e. remove delayed) 1034 // we don't check invalid entries (i.e. remove delayed)
1034 if (!(*it).isValid()) continue; 1035 if (!(*it).isValid()) continue;
1035 1036
1036 int ev = scanEntry( &(*it) ); 1037 int ev = scanEntry( &(*it) );
1037 1038
1038#ifdef HAVE_DNOTIFY 1039#ifdef HAVE_DNOTIFY
1039 if ((*it).m_mode == DNotifyMode) { 1040 if ((*it).m_mode == DNotifyMode) {
1040 if ((*it).isDir && (ev == Deleted)) { 1041 if ((*it).isDir && (ev == Deleted)) {
1041 dList.append( &(*it) ); 1042 dList.append( &(*it) );
1042 1043
1043 // must close the FD. 1044 // must close the FD.
1044 if ((*it).dn_fd) { 1045 if ((*it).dn_fd) {
1045 ::close((*it).dn_fd); 1046 ::close((*it).dn_fd);
1046 fd_Entry.remove((*it).dn_fd); 1047 fd_Entry.remove((*it).dn_fd);
1047 (*it).dn_fd = 0; 1048 (*it).dn_fd = 0;
1048 } 1049 }
1049 } 1050 }
1050 1051
1051 else if ((*it).isDir && (ev == Created)) { 1052 else if ((*it).isDir && (ev == Created)) {
1052 // For created, but yet without DNOTIFYing ... 1053 // For created, but yet without DNOTIFYing ...
1053 if ( (*it).dn_fd == 0) { 1054 if ( (*it).dn_fd == 0) {
1054 cList.append( &(*it) ); 1055 cList.append( &(*it) );
1055 if (! useDNotify( &(*it) )) { 1056 if (! useDNotify( &(*it) )) {
1056 // if DNotify setup fails... 1057 // if DNotify setup fails...
1057 useStat( &(*it) ); 1058 useStat( &(*it) );
1058 } 1059 }
1059 } 1060 }
1060 } 1061 }
1061 } 1062 }
1062#endif 1063#endif
1063 1064
1064 if ( ev != NoChange ) 1065 if ( ev != NoChange )
1065 emitEvent( &(*it), ev); 1066 emitEvent( &(*it), ev);
1066 } 1067 }
1067 1068
1068 1069
1069#ifdef HAVE_DNOTIFY 1070#ifdef HAVE_DNOTIFY
1070 // Scan parent of deleted directories for new creation 1071 // Scan parent of deleted directories for new creation
1071 Entry* e; 1072 Entry* e;
1072 for(e=dList.first();e;e=dList.next()) 1073 for(e=dList.first();e;e=dList.next())
1073 addEntry(0, QDir::cleanDirPath( e->path+"/.."), e, true); 1074 addEntry(0, QDir::cleanDirPath( e->path+"/.."), e, true);
1074 1075
1075 // Remove watch of parent of new created directories 1076 // Remove watch of parent of new created directories
1076 for(e=cList.first();e;e=cList.next()) 1077 for(e=cList.first();e;e=cList.next())
1077 removeEntry(0, QDir::cleanDirPath( e->path+"/.."), e); 1078 removeEntry(0, QDir::cleanDirPath( e->path+"/.."), e);
1078#endif 1079#endif
1079 1080
1080 if ( timerRunning ) 1081 if ( timerRunning )
1081 timer->start(freq); 1082 timer->start(freq);
1082 1083
1083 QTimer::singleShot(0, this, SLOT(slotRemoveDelayed())); 1084 QTimer::singleShot(0, this, SLOT(slotRemoveDelayed()));
1084} 1085}
1085 1086
1086#ifdef HAVE_FAM 1087#ifdef HAVE_FAM
1087void KDirWatchPrivate::famEventReceived() 1088void KDirWatchPrivate::famEventReceived()
1088{ 1089{
1089 static FAMEvent fe; 1090 static FAMEvent fe;
1090 1091
1091 delayRemove = true; 1092 delayRemove = true;
1092 1093
1093 while(use_fam && FAMPending(&fc)) { 1094 while(use_fam && FAMPending(&fc)) {
1094 if (FAMNextEvent(&fc, &fe) == -1) { 1095 if (FAMNextEvent(&fc, &fe) == -1) {
1095 kdWarning(7001) << "FAM connection problem, switching to polling." 1096 kdWarning(7001) << "FAM connection problem, switching to polling."
1096 << endl; 1097 << endl;
1097 use_fam = false; 1098 use_fam = false;
1098 delete sn; sn = 0; 1099 delete sn; sn = 0;
1099 1100
1100 // Replace all FAMMode entries with DNotify/Stat 1101 // Replace all FAMMode entries with DNotify/Stat
1101 EntryMap::Iterator it; 1102 EntryMap::Iterator it;
1102 it = m_mapEntries.begin(); 1103 it = m_mapEntries.begin();
1103 for( ; it != m_mapEntries.end(); ++it ) 1104 for( ; it != m_mapEntries.end(); ++it )
1104 if ((*it).m_mode == FAMMode && (*it).m_clients.count()>0) { 1105 if ((*it).m_mode == FAMMode && (*it).m_clients.count()>0) {
1105#ifdef HAVE_DNOTIFY 1106#ifdef HAVE_DNOTIFY
1106 if (useDNotify( &(*it) )) continue; 1107 if (useDNotify( &(*it) )) continue;
1107#endif 1108#endif
1108 useStat( &(*it) ); 1109 useStat( &(*it) );
1109 } 1110 }
1110 } 1111 }
1111 else 1112 else
1112 checkFAMEvent(&fe); 1113 checkFAMEvent(&fe);
1113 } 1114 }
1114 1115
1115 QTimer::singleShot(0, this, SLOT(slotRemoveDelayed())); 1116 QTimer::singleShot(0, this, SLOT(slotRemoveDelayed()));
1116} 1117}
1117 1118
1118void KDirWatchPrivate::checkFAMEvent(FAMEvent* fe) 1119void KDirWatchPrivate::checkFAMEvent(FAMEvent* fe)
1119{ 1120{
1120 // Don't be too verbose ;-) 1121 // Don't be too verbose ;-)
1121 if ((fe->code == FAMExists) || 1122 if ((fe->code == FAMExists) ||
1122 (fe->code == FAMEndExist) || 1123 (fe->code == FAMEndExist) ||
1123 (fe->code == FAMAcknowledge)) return; 1124 (fe->code == FAMAcknowledge)) return;
1124 1125
1125 // $HOME/.X.err grows with debug output, so don't notify change 1126 // $HOME/.X.err grows with debug output, so don't notify change
1126 if ( *(fe->filename) == '.') { 1127 if ( *(fe->filename) == '.') {
1127 if (strncmp(fe->filename, ".X.err", 6) == 0) return; 1128 if (strncmp(fe->filename, ".X.err", 6) == 0) return;
1128 if (strncmp(fe->filename, ".xsession-errors", 16) == 0) return; 1129 if (strncmp(fe->filename, ".xsession-errors", 16) == 0) return;
1129 } 1130 }
1130 1131
1131 Entry* e = 0; 1132 Entry* e = 0;
1132 EntryMap::Iterator it = m_mapEntries.begin(); 1133 EntryMap::Iterator it = m_mapEntries.begin();
1133 for( ; it != m_mapEntries.end(); ++it ) 1134 for( ; it != m_mapEntries.end(); ++it )
1134 if (FAMREQUEST_GETREQNUM(&( (*it).fr )) == 1135 if (FAMREQUEST_GETREQNUM(&( (*it).fr )) ==
1135 FAMREQUEST_GETREQNUM(&(fe->fr)) ) { 1136 FAMREQUEST_GETREQNUM(&(fe->fr)) ) {
1136 e = &(*it); 1137 e = &(*it);
1137 break; 1138 break;
1138 } 1139 }
1139 1140
1140 // Entry* e = static_cast<Entry*>(fe->userdata); 1141 // Entry* e = static_cast<Entry*>(fe->userdata);
1141 1142
1142 kdDebug(7001) << "Processing FAM event (" 1143 kdDebug(7001) << "Processing FAM event ("
1143 << ((fe->code == FAMChanged) ? "FAMChanged" : 1144 << ((fe->code == FAMChanged) ? "FAMChanged" :
1144 (fe->code == FAMDeleted) ? "FAMDeleted" : 1145 (fe->code == FAMDeleted) ? "FAMDeleted" :
1145 (fe->code == FAMStartExecuting) ? "FAMStartExecuting" : 1146 (fe->code == FAMStartExecuting) ? "FAMStartExecuting" :
1146 (fe->code == FAMStopExecuting) ? "FAMStopExecuting" : 1147 (fe->code == FAMStopExecuting) ? "FAMStopExecuting" :
1147 (fe->code == FAMCreated) ? "FAMCreated" : 1148 (fe->code == FAMCreated) ? "FAMCreated" :
1148 (fe->code == FAMMoved) ? "FAMMoved" : 1149 (fe->code == FAMMoved) ? "FAMMoved" :
1149 (fe->code == FAMAcknowledge) ? "FAMAcknowledge" : 1150 (fe->code == FAMAcknowledge) ? "FAMAcknowledge" :
1150 (fe->code == FAMExists) ? "FAMExists" : 1151 (fe->code == FAMExists) ? "FAMExists" :
1151 (fe->code == FAMEndExist) ? "FAMEndExist" : "Unknown Code") 1152 (fe->code == FAMEndExist) ? "FAMEndExist" : "Unknown Code")
1152 << ", " << fe->filename 1153 << ", " << fe->filename
1153 << ", Req " << FAMREQUEST_GETREQNUM(&(fe->fr)) 1154 << ", Req " << FAMREQUEST_GETREQNUM(&(fe->fr))
1154 << ")" << endl; 1155 << ")" << endl;
1155 1156
1156 if (!e) { 1157 if (!e) {
1157 // this happens e.g. for FAMAcknowledge after deleting a dir... 1158 // this happens e.g. for FAMAcknowledge after deleting a dir...
1158 // kdDebug(7001) << "No entry for FAM event ?!" << endl; 1159 // kdDebug(7001) << "No entry for FAM event ?!" << endl;
1159 return; 1160 return;
1160 } 1161 }
1161 1162
1162 if (e->m_status == NonExistent) { 1163 if (e->m_status == NonExistent) {
1163 kdDebug(7001) << "FAM event for nonExistent entry " << e->path << endl; 1164 kdDebug(7001) << "FAM event for nonExistent entry " << e->path << endl;
1164 return; 1165 return;
1165 } 1166 }
1166 1167
1167 if (e->isDir) 1168 if (e->isDir)
1168 switch (fe->code) 1169 switch (fe->code)
1169 { 1170 {
1170 case FAMDeleted: 1171 case FAMDeleted:
1171 // file absolute: watched dir 1172 // file absolute: watched dir
1172 if (fe->filename[0] == '/') 1173 if (fe->filename[0] == '/')
1173 { 1174 {
1174 // a watched directory was deleted 1175 // a watched directory was deleted
1175 1176
1176 e->m_status = NonExistent; 1177 e->m_status = NonExistent;
1177 FAMCancelMonitor(&fc, &(e->fr) ); // needed ? 1178 FAMCancelMonitor(&fc, &(e->fr) ); // needed ?
1178 kdDebug(7001) << "Cancelled FAMReq " 1179 kdDebug(7001) << "Cancelled FAMReq "
1179 << FAMREQUEST_GETREQNUM(&(e->fr)) 1180 << FAMREQUEST_GETREQNUM(&(e->fr))
1180 << " for " << e->path << endl; 1181 << " for " << e->path << endl;
1181 // Scan parent for a new creation 1182 // Scan parent for a new creation
1182 addEntry(0, QDir::cleanDirPath( e->path+"/.."), e, true); 1183 addEntry(0, QDir::cleanDirPath( e->path+"/.."), e, true);
1183 } 1184 }
1184 emitEvent(e, Deleted, QFile::decodeName(fe->filename)); 1185 emitEvent(e, Deleted, QFile::decodeName(fe->filename));
1185 break; 1186 break;
1186 1187
1187 case FAMCreated: { 1188 case FAMCreated: {
1188 // check for creation of a directory we have to watch 1189 // check for creation of a directory we have to watch
1189 Entry *sub_entry = e->m_entries.first(); 1190 Entry *sub_entry = e->m_entries.first();
1190 for(;sub_entry; sub_entry = e->m_entries.next()) 1191 for(;sub_entry; sub_entry = e->m_entries.next())
1191 if (sub_entry->path == e->path + "/" + fe->filename) break; 1192 if (sub_entry->path == e->path + "/" + fe->filename) break;
1192 if (sub_entry && sub_entry->isDir) { 1193 if (sub_entry && sub_entry->isDir) {
1193 QString path = e->path; 1194 QString path = e->path;
1194 removeEntry(0,e->path,sub_entry); // <e> can be invalid here!! 1195 removeEntry(0,e->path,sub_entry); // <e> can be invalid here!!
1195 sub_entry->m_status = Normal; 1196 sub_entry->m_status = Normal;
1196 if (!useFAM(sub_entry)) 1197 if (!useFAM(sub_entry))
1197 useStat(sub_entry); 1198 useStat(sub_entry);
1198 1199
1199 emitEvent(sub_entry, Created); 1200 emitEvent(sub_entry, Created);
1200 } 1201 }
1201 else emitEvent(e, Created, QFile::decodeName(fe->filename)); 1202 else emitEvent(e, Created, QFile::decodeName(fe->filename));
1202 break; 1203 break;
1203 } 1204 }
1204 1205
1205 case FAMChanged: 1206 case FAMChanged:
1206 emitEvent(e, Changed, QFile::decodeName(fe->filename)); 1207 emitEvent(e, Changed, QFile::decodeName(fe->filename));
1207 1208
1208 default: 1209 default:
1209 break; 1210 break;
1210 } 1211 }
1211 else switch (fe->code) 1212 else switch (fe->code)
1212 { 1213 {
1213 case FAMCreated: emitEvent(e, Created); 1214 case FAMCreated: emitEvent(e, Created);
1214 break; 1215 break;
1215 case FAMDeleted: emitEvent(e, Deleted); 1216 case FAMDeleted: emitEvent(e, Deleted);
1216 break; 1217 break;
1217 case FAMChanged: emitEvent(e, Changed); 1218 case FAMChanged: emitEvent(e, Changed);
1218 break; 1219 break;
1219 default: break; 1220 default: break;
1220 } 1221 }
1221} 1222}
1222#else 1223#else
1223void KDirWatchPrivate::famEventReceived() {} 1224void KDirWatchPrivate::famEventReceived() {}
1224#endif 1225#endif
1225 1226
1226 1227
1227void KDirWatchPrivate::statistics() 1228void KDirWatchPrivate::statistics()
1228{ 1229{
1229 EntryMap::Iterator it; 1230 EntryMap::Iterator it;
1230 1231
1231 kdDebug(7001) << "Entries watched:" << endl; 1232 kdDebug(7001) << "Entries watched:" << endl;
1232 if (m_mapEntries.count()==0) { 1233 if (m_mapEntries.count()==0) {
1233 kdDebug(7001) << " None." << endl; 1234 kdDebug(7001) << " None." << endl;
1234 } 1235 }
1235 else { 1236 else {
1236 it = m_mapEntries.begin(); 1237 it = m_mapEntries.begin();
1237 for( ; it != m_mapEntries.end(); ++it ) { 1238 for( ; it != m_mapEntries.end(); ++it ) {
1238 Entry* e = &(*it); 1239 Entry* e = &(*it);
1239 kdDebug(7001) << " " << e->path << " (" 1240 kdDebug(7001) << " " << e->path << " ("
1240 << ((e->m_status==Normal)?"":"Nonexistent ") 1241 << ((e->m_status==Normal)?"":"Nonexistent ")
1241 << (e->isDir ? "Dir":"File") << ", using " 1242 << (e->isDir ? "Dir":"File") << ", using "
1242 << ((e->m_mode == FAMMode) ? "FAM" : 1243 << ((e->m_mode == FAMMode) ? "FAM" :
1243 (e->m_mode == DNotifyMode) ? "DNotify" : 1244 (e->m_mode == DNotifyMode) ? "DNotify" :
1244 (e->m_mode == StatMode) ? "Stat" : "Unknown Method") 1245 (e->m_mode == StatMode) ? "Stat" : "Unknown Method")
1245 << ")" << endl; 1246 << ")" << endl;
1246 1247
1247 Client* c = e->m_clients.first(); 1248 Client* c = e->m_clients.first();
1248 for(;c; c = e->m_clients.next()) { 1249 for(;c; c = e->m_clients.next()) {
1249 QString pending; 1250 QString pending;
1250 if (c->watchingStopped) { 1251 if (c->watchingStopped) {
1251 if (c->pending & Deleted) pending += "deleted "; 1252 if (c->pending & Deleted) pending += "deleted ";
1252 if (c->pending & Created) pending += "created "; 1253 if (c->pending & Created) pending += "created ";
1253 if (c->pending & Changed) pending += "changed "; 1254 if (c->pending & Changed) pending += "changed ";
1254 if (!pending.isEmpty()) pending = " (pending: " + pending + ")"; 1255 if (!pending.isEmpty()) pending = " (pending: " + pending + ")";
1255 pending = ", stopped" + pending; 1256 pending = ", stopped" + pending;
1256 } 1257 }
1257 kdDebug(7001) << " by " << c->instance->name() 1258 kdDebug(7001) << " by " << c->instance->name()
1258 << " (" << c->count << " times)" 1259 << " (" << c->count << " times)"
1259 << pending << endl; 1260 << pending << endl;
1260 } 1261 }
1261 if (e->m_entries.count()>0) { 1262 if (e->m_entries.count()>0) {
1262 kdDebug(7001) << " dependent entries:" << endl; 1263 kdDebug(7001) << " dependent entries:" << endl;
1263 Entry* d = e->m_entries.first(); 1264 Entry* d = e->m_entries.first();
1264 for(;d; d = e->m_entries.next()) { 1265 for(;d; d = e->m_entries.next()) {
1265 kdDebug(7001) << " " << d->path << endl; 1266 kdDebug(7001) << " " << d->path << endl;
1266 } 1267 }
1267 } 1268 }
1268 } 1269 }
1269 } 1270 }
1270} 1271}
1271 1272
1272 1273
1273// 1274//
1274// Class KDirWatch 1275// Class KDirWatch
1275// 1276//
1276 1277
1277static KStaticDeleter<KDirWatch> sd_dw; 1278static KStaticDeleter<KDirWatch> sd_dw;
1278KDirWatch* KDirWatch::s_pSelf = 0L; 1279KDirWatch* KDirWatch::s_pSelf = 0L;
1279 1280
1280KDirWatch* KDirWatch::self() 1281KDirWatch* KDirWatch::self()
1281{ 1282{
1282 if ( !s_pSelf ) { 1283 if ( !s_pSelf ) {
1283//US sd_dw.setObject( s_pSelf, new KDirWatch ); 1284//US sd_dw.setObject( s_pSelf, new KDirWatch );
1284 s_pSelf = sd_dw.setObject( new KDirWatch ); 1285 s_pSelf = sd_dw.setObject( new KDirWatch );
1285 } 1286 }
1286 1287
1287 return s_pSelf; 1288 return s_pSelf;
1288} 1289}
1289 1290
1290bool KDirWatch::exists() 1291bool KDirWatch::exists()
1291{ 1292{
1292 return s_pSelf != 0; 1293 return s_pSelf != 0;
1293} 1294}
1294 1295
1295KDirWatch::KDirWatch (QObject* parent, const char* name) 1296KDirWatch::KDirWatch (QObject* parent, const char* name)
1296 : QObject(parent,name) 1297 : QObject(parent,name)
1297{ 1298{
1298 if (!name) { 1299 if (!name) {
1299 static int nameCounter = 0; 1300 static int nameCounter = 0;
1300 1301
1301 nameCounter++; 1302 nameCounter++;
1302 setName(QString("KDirWatch-%1").arg(nameCounter).ascii()); 1303 setName(QString("KDirWatch-%1").arg(nameCounter).ascii());
1303 } 1304 }
1304 1305
1305 if (!dwp_self) 1306 if (!dwp_self)
1306 dwp_self = new KDirWatchPrivate; 1307 dwp_self = new KDirWatchPrivate;
1307 d = dwp_self; 1308 d = dwp_self;
1308 d->ref(); 1309 d->ref();
1309 1310
1310 _isStopped = false; 1311 _isStopped = false;
1311} 1312}
1312 1313
1313KDirWatch::~KDirWatch() 1314KDirWatch::~KDirWatch()
1314{ 1315{
1315 if (d) d->removeEntries(this); 1316 if (d) d->removeEntries(this);
1316 if ( d->deref() ) 1317 if ( d->deref() )
1317 { 1318 {
1318 // delete it if it's the last one 1319 // delete it if it's the last one
1319 delete d; 1320 delete d;
1320 dwp_self = 0L; 1321 dwp_self = 0L;
1321 } 1322 }
1322} 1323}
1323 1324
1324 1325
1325// TODO: add watchFiles/recursive support 1326// TODO: add watchFiles/recursive support
1326void KDirWatch::addDir( const QString& _path, 1327void KDirWatch::addDir( const QString& _path,
1327 bool watchFiles, bool recursive) 1328 bool watchFiles, bool recursive)
1328{ 1329{
1329 if (watchFiles || recursive) { 1330 if (watchFiles || recursive) {
1330 kdDebug(7001) << "addDir - recursive/watchFiles not supported in KDE 3.0" 1331 kdDebug(7001) << "addDir - recursive/watchFiles not supported in KDE 3.0"
1331 << endl; 1332 << endl;
1332 } 1333 }
1333 if (d) d->addEntry(this, _path, 0, true); 1334 if (d) d->addEntry(this, _path, 0, true);
1334} 1335}
1335 1336
1336void KDirWatch::addFile( const QString& _path ) 1337void KDirWatch::addFile( const QString& _path )
1337{ 1338{
1338 if (d) d->addEntry(this, _path, 0, false); 1339 if (d) d->addEntry(this, _path, 0, false);
1339} 1340}
1340 1341
1341QDateTime KDirWatch::ctime( const QString &_path ) 1342QDateTime KDirWatch::ctime( const QString &_path )
1342{ 1343{
1343 KDirWatchPrivate::Entry* e = d->entry(_path); 1344 KDirWatchPrivate::Entry* e = d->entry(_path);
1344 1345
1345 if (!e) 1346 if (!e)
1346 return QDateTime(); 1347 return QDateTime();
1347 1348
1348 QDateTime result; 1349 QDateTime result;
1349 result.setTime_t(e->m_ctime); 1350 result.setTime_t(e->m_ctime);
1350 return result; 1351 return result;
1351} 1352}
1352 1353
1353void KDirWatch::removeDir( const QString& _path ) 1354void KDirWatch::removeDir( const QString& _path )
1354{ 1355{
1355 if (d) d->removeEntry(this, _path, 0); 1356 if (d) d->removeEntry(this, _path, 0);
1356} 1357}
1357 1358
1358void KDirWatch::removeFile( const QString& _path ) 1359void KDirWatch::removeFile( const QString& _path )
1359{ 1360{
1360 if (d) d->removeEntry(this, _path, 0); 1361 if (d) d->removeEntry(this, _path, 0);
1361} 1362}
1362 1363
1363bool KDirWatch::stopDirScan( const QString& _path ) 1364bool KDirWatch::stopDirScan( const QString& _path )
1364{ 1365{
1365 if (d) { 1366 if (d) {
1366 KDirWatchPrivate::Entry *e = d->entry(_path); 1367 KDirWatchPrivate::Entry *e = d->entry(_path);
1367 if (e && e->isDir) return d->stopEntryScan(this, e); 1368 if (e && e->isDir) return d->stopEntryScan(this, e);
1368 } 1369 }
1369 return false; 1370 return false;
1370} 1371}
1371 1372
1372bool KDirWatch::restartDirScan( const QString& _path ) 1373bool KDirWatch::restartDirScan( const QString& _path )
1373{ 1374{
1374 if (d) { 1375 if (d) {
1375 KDirWatchPrivate::Entry *e = d->entry(_path); 1376 KDirWatchPrivate::Entry *e = d->entry(_path);
1376 if (e && e->isDir) 1377 if (e && e->isDir)
1377 // restart without notifying pending events 1378 // restart without notifying pending events
1378 return d->restartEntryScan(this, e, false); 1379 return d->restartEntryScan(this, e, false);
1379 } 1380 }
1380 return false; 1381 return false;
1381} 1382}
1382 1383
1383void KDirWatch::stopScan() 1384void KDirWatch::stopScan()
1384{ 1385{
1385 if (d) d->stopScan(this); 1386 if (d) d->stopScan(this);
1386 _isStopped = true; 1387 _isStopped = true;
1387} 1388}
1388 1389
1389void KDirWatch::startScan( bool notify, bool skippedToo ) 1390void KDirWatch::startScan( bool notify, bool skippedToo )
1390{ 1391{
1391 _isStopped = false; 1392 _isStopped = false;
1392 if (d) d->startScan(this, notify, skippedToo); 1393 if (d) d->startScan(this, notify, skippedToo);
1393} 1394}
1394 1395
1395 1396
1396bool KDirWatch::contains( const QString& _path ) const 1397bool KDirWatch::contains( const QString& _path ) const
1397{ 1398{
1398 KDirWatchPrivate::Entry* e = d->entry(_path); 1399 KDirWatchPrivate::Entry* e = d->entry(_path);
1399 if (!e) 1400 if (!e)
1400 return false; 1401 return false;
1401 1402
1402 KDirWatchPrivate::Client* c = e->m_clients.first(); 1403 KDirWatchPrivate::Client* c = e->m_clients.first();
1403 for(;c;c=e->m_clients.next()) 1404 for(;c;c=e->m_clients.next())
1404 if (c->instance == this) return true; 1405 if (c->instance == this) return true;
1405 1406
1406 return false; 1407 return false;
1407} 1408}
1408 1409
1409void KDirWatch::statistics() 1410void KDirWatch::statistics()
1410{ 1411{
1411 if (!dwp_self) { 1412 if (!dwp_self) {
1412 kdDebug(7001) << "KDirWatch not used" << endl; 1413 kdDebug(7001) << "KDirWatch not used" << endl;
1413 return; 1414 return;
1414 } 1415 }
1415 dwp_self->statistics(); 1416 dwp_self->statistics();
1416} 1417}
1417 1418
1418 1419
1419void KDirWatch::setCreated( const QString & _file ) 1420void KDirWatch::setCreated( const QString & _file )
1420{ 1421{
1421 kdDebug(7001) << name() << " emitting created " << _file << endl; 1422 kdDebug(7001) << name() << " emitting created " << _file << endl;
1422 emit created( _file ); 1423 emit created( _file );
1423} 1424}
1424 1425
1425void KDirWatch::setDirty( const QString & _file ) 1426void KDirWatch::setDirty( const QString & _file )
1426{ 1427{
1427 kdDebug(7001) << name() << " emitting dirty " << _file << endl; 1428 kdDebug(7001) << name() << " emitting dirty " << _file << endl;
1428 emit dirty( _file ); 1429 emit dirty( _file );
1429} 1430}
1430 1431
1431void KDirWatch::setDeleted( const QString & _file ) 1432void KDirWatch::setDeleted( const QString & _file )
1432{ 1433{
1433 kdDebug(7001) << name() << " emitting deleted " << _file << endl; 1434 kdDebug(7001) << name() << " emitting deleted " << _file << endl;
1434 emit deleted( _file ); 1435 emit deleted( _file );
1435} 1436}
1436 1437
1437//US #include "kdirwatch.moc" 1438//US #include "kdirwatch.moc"
1438//US #include "kdirwatch_p.moc" 1439//US #include "kdirwatch_p.moc"
1439 1440
1440//sven 1441//sven
1441 1442
1442// vim: sw=2 ts=8 et 1443// vim: sw=2 ts=8 et