Qt 4.8
qfilesystemwatcher.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "qfilesystemwatcher.h"
43 #include "qfilesystemwatcher_p.h"
44 
45 #ifndef QT_NO_FILESYSTEMWATCHER
46 
47 #include <qdatetime.h>
48 #include <qdebug.h>
49 #include <qdir.h>
50 #include <qfileinfo.h>
51 #include <qmutex.h>
52 #include <qset.h>
53 #include <qtimer.h>
54 
55 #if defined(Q_OS_WIN)
57 #elif defined(Q_OS_LINUX)
60 #elif defined(Q_OS_QNX) && !defined(QT_NO_INOTIFY)
62 #elif defined(Q_OS_FREEBSD) || defined(Q_OS_MAC)
63 # if (defined Q_OS_MAC) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
65 # endif //MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
67 #elif defined(Q_OS_SYMBIAN)
68 # include "qfilesystemwatcher_symbian_p.h"
69 #endif
70 
72 
73 enum { PollingInterval = 1000 };
74 
76 {
77  Q_OBJECT
78 
79  class FileInfo
80  {
83  QFile::Permissions permissions;
86 
87  public:
88  FileInfo(const QFileInfo &fileInfo)
89  : ownerId(fileInfo.ownerId()),
90  groupId(fileInfo.groupId()),
91  permissions(fileInfo.permissions()),
92  lastModified(fileInfo.lastModified())
93  {
94  if (fileInfo.isDir()) {
95  entries = fileInfo.absoluteDir().entryList(QDir::AllEntries);
96  }
97  }
98  FileInfo &operator=(const QFileInfo &fileInfo)
99  {
100  *this = FileInfo(fileInfo);
101  return *this;
102  }
103 
104  bool operator!=(const QFileInfo &fileInfo) const
105  {
106  if (fileInfo.isDir() && entries != fileInfo.absoluteDir().entryList(QDir::AllEntries))
107  return true;
108  return (ownerId != fileInfo.ownerId()
109  || groupId != fileInfo.groupId()
110  || permissions != fileInfo.permissions()
111  || lastModified != fileInfo.lastModified());
112  }
113  };
114 
115  mutable QMutex mutex;
117 
118 public:
120 
121  void run();
122 
123  QStringList addPaths(const QStringList &paths, QStringList *files, QStringList *directories);
124  QStringList removePaths(const QStringList &paths, QStringList *files, QStringList *directories);
125 
126  void stop();
127 
128 private Q_SLOTS:
129  void timeout();
130 };
131 
133 {
134 #ifndef QT_NO_THREAD
135  moveToThread(this);
136 #endif
137 }
138 
140 {
141  QTimer timer;
142  connect(&timer, SIGNAL(timeout()), SLOT(timeout()));
143  timer.start(PollingInterval);
144  (void) exec();
145 }
146 
150 {
151  QMutexLocker locker(&mutex);
152  QStringList p = paths;
153  QMutableListIterator<QString> it(p);
154  while (it.hasNext()) {
155  QString path = it.next();
156  QFileInfo fi(path);
157  if (!fi.exists())
158  continue;
159  if (fi.isDir()) {
160  if (!directories->contains(path))
161  directories->append(path);
162  if (!path.endsWith(QLatin1Char('/')))
163  fi = QFileInfo(path + QLatin1Char('/'));
164  this->directories.insert(path, fi);
165  } else {
166  if (!files->contains(path))
167  files->append(path);
168  this->files.insert(path, fi);
169  }
170  it.remove();
171  }
172  start();
173  return p;
174 }
175 
179 {
180  QMutexLocker locker(&mutex);
181  QStringList p = paths;
182  QMutableListIterator<QString> it(p);
183  while (it.hasNext()) {
184  QString path = it.next();
185  if (this->directories.remove(path)) {
186  directories->removeAll(path);
187  it.remove();
188  } else if (this->files.remove(path)) {
189  files->removeAll(path);
190  it.remove();
191  }
192  }
193  if (this->files.isEmpty() && this->directories.isEmpty()) {
194  locker.unlock();
195  stop();
196  wait();
197  }
198  return p;
199 }
200 
202 {
203  quit();
204 }
205 
207 {
208  QMutexLocker locker(&mutex);
209  QMutableHashIterator<QString, FileInfo> fit(files);
210  while (fit.hasNext()) {
211  QHash<QString, FileInfo>::iterator x = fit.next();
212  QString path = x.key();
213  QFileInfo fi(path);
214  if (!fi.exists()) {
215  fit.remove();
216  emit fileChanged(path, true);
217  } else if (x.value() != fi) {
218  x.value() = fi;
219  emit fileChanged(path, false);
220  }
221  }
222  QMutableHashIterator<QString, FileInfo> dit(directories);
223  while (dit.hasNext()) {
224  QHash<QString, FileInfo>::iterator x = dit.next();
225  QString path = x.key();
226  QFileInfo fi(path);
227  if (!path.endsWith(QLatin1Char('/')))
228  fi = QFileInfo(path + QLatin1Char('/'));
229  if (!fi.exists()) {
230  dit.remove();
231  emit directoryChanged(path, true);
232  } else if (x.value() != fi) {
233  fi.refresh();
234  if (!fi.exists()) {
235  dit.remove();
236  emit directoryChanged(path, true);
237  } else {
238  x.value() = fi;
239  emit directoryChanged(path, false);
240  }
241  }
242 
243  }
244 }
245 
246 
247 
248 
250 {
251 #if defined(Q_OS_WIN)
253 #elif defined(Q_OS_QNX) && !defined(QT_NO_INOTIFY)
255 #elif defined(Q_OS_LINUX)
257  if(!eng)
259  return eng;
260 #elif defined(Q_OS_FREEBSD) || defined(Q_OS_MAC)
261 # if 0 && defined(Q_OS_MAC) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
264  else
265 # endif
267 #elif defined(Q_OS_SYMBIAN)
268  return new QSymbianFileSystemWatcherEngine;
269 #else
270  return 0;
271 #endif
272 }
273 
275  : native(0), poller(0), forced(0)
276 {
277 }
278 
280 {
283  if (native) {
285  SIGNAL(fileChanged(QString,bool)),
286  q,
287  SLOT(_q_fileChanged(QString,bool)));
289  SIGNAL(directoryChanged(QString,bool)),
290  q,
292  }
293 }
294 
296 {
297  if(forced)
298  return;
299 
301 
302 #if defined(Q_OS_LINUX)
303  if(forceName == QLatin1String("inotify")) {
305  } else if(forceName == QLatin1String("dnotify")) {
307  }
308 #else
309  Q_UNUSED(forceName);
310 #endif
311 
312  if(forced) {
314  SIGNAL(fileChanged(QString,bool)),
315  q,
316  SLOT(_q_fileChanged(QString,bool)));
318  SIGNAL(directoryChanged(QString,bool)),
319  q,
321  }
322 }
323 
325 {
326  if(poller)
327  return;
328 
330  poller = new QPollingFileSystemWatcherEngine; // that was a mouthful
332  SIGNAL(fileChanged(QString,bool)),
333  q,
334  SLOT(_q_fileChanged(QString,bool)));
336  SIGNAL(directoryChanged(QString,bool)),
337  q,
339 }
340 
341 void QFileSystemWatcherPrivate::_q_fileChanged(const QString &path, bool removed)
342 {
344  if (!files.contains(path)) {
345  // the path was removed after a change was detected, but before we delivered the signal
346  return;
347  }
348  if (removed)
349  files.removeAll(path);
350  emit q->fileChanged(path);
351 }
352 
354 {
356  if (!directories.contains(path)) {
357  // perhaps the path was removed after a change was detected, but before we delivered the signal
358  return;
359  }
360  if (removed)
361  directories.removeAll(path);
362  emit q->directoryChanged(path);
363 }
364 
365 
366 
424  : QObject(*new QFileSystemWatcherPrivate, parent)
425 {
426  d_func()->init();
427 }
428 
434  : QObject(*new QFileSystemWatcherPrivate, parent)
435 {
436  d_func()->init();
437  addPaths(paths);
438 }
439 
444 {
446  if (d->native) {
447  d->native->stop();
448  d->native->wait();
449  delete d->native;
450  d->native = 0;
451  }
452  if (d->poller) {
453  d->poller->stop();
454  d->poller->wait();
455  delete d->poller;
456  d->poller = 0;
457  }
458  if (d->forced) {
459  d->forced->stop();
460  d->forced->wait();
461  delete d->forced;
462  d->forced = 0;
463  }
464 }
465 
484 {
485  if (path.isEmpty()) {
486  qWarning("QFileSystemWatcher::addPath: path is empty");
487  return;
488  }
489  addPaths(QStringList(path));
490 }
491 
511 {
513  if (paths.isEmpty()) {
514  qWarning("QFileSystemWatcher::addPaths: list is empty");
515  return;
516  }
517 
518  QStringList p = paths;
519  QFileSystemWatcherEngine *engine = 0;
520 
521  if(!objectName().startsWith(QLatin1String("_qt_autotest_force_engine_"))) {
522  // Normal runtime case - search intelligently for best engine
523  if(d->native) {
524  engine = d->native;
525  } else {
526  d_func()->initPollerEngine();
527  engine = d->poller;
528  }
529 
530  } else {
531  // Autotest override case - use the explicitly selected engine only
532  QString forceName = objectName().mid(26);
533  if(forceName == QLatin1String("poller")) {
534  qDebug() << "QFileSystemWatcher: skipping native engine, using only polling engine";
535  d_func()->initPollerEngine();
536  engine = d->poller;
537  } else if(forceName == QLatin1String("native")) {
538  qDebug() << "QFileSystemWatcher: skipping polling engine, using only native engine";
539  engine = d->native;
540  } else {
541  qDebug() << "QFileSystemWatcher: skipping polling and native engine, using only explicit" << forceName << "engine";
542  d_func()->initForcedEngine(forceName);
543  engine = d->forced;
544  }
545  }
546 
547  if(engine)
548  p = engine->addPaths(p, &d->files, &d->directories);
549 
550  if (!p.isEmpty())
551  qWarning("QFileSystemWatcher: failed to add paths: %s",
552  qPrintable(p.join(QLatin1String(", "))));
553 }
554 
561 {
562  if (path.isEmpty()) {
563  qWarning("QFileSystemWatcher::removePath: path is empty");
564  return;
565  }
566  removePaths(QStringList(path));
567 }
568 
575 {
576  if (paths.isEmpty()) {
577  qWarning("QFileSystemWatcher::removePaths: list is empty");
578  return;
579  }
581  QStringList p = paths;
582  if (d->native)
583  p = d->native->removePaths(p, &d->files, &d->directories);
584  if (d->poller)
585  p = d->poller->removePaths(p, &d->files, &d->directories);
586  if (d->forced)
587  p = d->forced->removePaths(p, &d->files, &d->directories);
588 }
589 
641 {
642  Q_D(const QFileSystemWatcher);
643  return d->directories;
644 }
645 
647 {
648  Q_D(const QFileSystemWatcher);
649  return d->files;
650 }
651 
653 
654 #include "moc_qfilesystemwatcher.cpp"
655 
656 #include "qfilesystemwatcher.moc"
657 
658 #endif // QT_NO_FILESYSTEMWATCHER
659 
double d
Definition: qnumeric_p.h:62
void fileChanged(const QString &path, bool removed)
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
The QMutex class provides access serialization between threads.
Definition: qmutex.h:60
QHash< QString, FileInfo > files
The QFileSystemWatcher class provides an interface for monitoring files and directories for modificat...
#define it(className, varName)
void refresh()
Refreshes the information about the file, i.e.
Definition: qfileinfo.cpp:695
void removePath(const QString &file)
Removes the specified path from the file system watcher.
static QFSEventsFileSystemWatcherEngine * create()
QDir absoluteDir() const
Returns the file&#39;s absolute path as a QDir object.
Definition: qfileinfo.cpp:873
#define SLOT(a)
Definition: qobjectdefs.h:226
void unlock()
Unlocks this mutex locker.
Definition: qmutex.h:117
void removePaths(const QStringList &files)
Removes the specified paths from the file system watcher.
void insert(int i, const T &t)
Inserts value at index position i in the list.
Definition: qlist.h:575
QLatin1String(DBUS_INTERFACE_DBUS))) Q_GLOBAL_STATIC_WITH_ARGS(QString
bool exists() const
Returns true if the file exists; otherwise returns false.
Definition: qfileinfo.cpp:675
void initForcedEngine(const QString &)
static QInotifyFileSystemWatcherEngine * create()
EventLoopTimerRef timer
T & value() const
Returns a modifiable reference to the current item&#39;s value.
Definition: qhash.h:348
#define Q_SLOTS
Definition: qobjectdefs.h:71
The QString class provides a Unicode character string.
Definition: qstring.h:83
The QHash class is a template class that provides a hash-table-based dictionary.
Definition: qdatastream.h:66
QFile::Permissions permissions() const
Returns the complete OR-ed together combination of QFile::Permissions for the file.
Definition: qfileinfo.cpp:1228
The QObject class is the base class of all Qt objects.
Definition: qobject.h:111
void run()
The starting point for the thread.
#define Q_D(Class)
Definition: qglobal.h:2482
void quit()
Tells the thread&#39;s event loop to exit with return code 0 (success).
Definition: qthread.cpp:614
QFileSystemWatcherEngine * native
bool isEmpty() const
Returns true if the list contains no items; otherwise returns false.
Definition: qlist.h:152
#define Q_Q(Class)
Definition: qglobal.h:2483
~QFileSystemWatcher()
Destroys the file system watcher.
Q_CORE_EXPORT void qDebug(const char *,...)
#define SIGNAL(a)
Definition: qobjectdefs.h:227
uint ownerId() const
Returns the id of the owner of the file.
Definition: qfileinfo.cpp:1144
QStringList addPaths(const QStringList &paths, QStringList *files, QStringList *directories)
void append(const T &t)
Inserts value at the end of the list.
Definition: qlist.h:507
void _q_fileChanged(const QString &path, bool removed)
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
QHash< QString, FileInfo > directories
bool isDir() const
Returns true if this object points to a directory or to a symbolic link to a directory; otherwise ret...
Definition: qfileinfo.cpp:990
static bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
Creates a connection of the given type from the signal in the sender object to the method in the rece...
Definition: qobject.cpp:2580
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition: qstring.h:704
QStringList files() const
Returns a list of paths to files that are being watched.
#define emit
Definition: qobjectdefs.h:76
The QStringList class provides a list of strings.
Definition: qstringlist.h:66
Q_CORE_EXPORT void qWarning(const char *,...)
unsigned int uint
Definition: qglobal.h:996
The QLatin1String class provides a thin wrapper around an US-ASCII/Latin-1 encoded string literal...
Definition: qstring.h:654
QBool contains(const QString &str, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the list contains the string str; otherwise returns false.
Definition: qstringlist.h:172
void moveToThread(QThread *thread)
Changes the thread affinity for this object and its children.
Definition: qobject.cpp:1458
void _q_directoryChanged(const QString &path, bool removed)
const Key & key() const
Returns the current item&#39;s key as a const reference.
Definition: qhash.h:347
QFileSystemWatcher(QObject *parent=0)
Constructs a new file system watcher object with the given parent.
QFileSystemWatcherEngine * poller
void directoryChanged(const QString &path, bool removed)
QFileSystemWatcherEngine * forced
#define Q_OBJECT
Definition: qobjectdefs.h:157
static QFileSystemWatcherEngine * createNativeEngine()
QString join(const QString &sep) const
Joins all the string list&#39;s strings into a single string with each element separated by the given sep...
Definition: qstringlist.h:162
QString mid(int position, int n=-1) const Q_REQUIRED_RESULT
Returns a string that contains n characters of this string, starting at the specified position index...
Definition: qstring.cpp:3706
static QDnotifyFileSystemWatcherEngine * create()
FileInfo & operator=(const QFileInfo &fileInfo)
The QMutexLocker class is a convenience class that simplifies locking and unlocking mutexes...
Definition: qmutex.h:101
void addPaths(const QStringList &files)
Adds each path in paths to the file system watcher.
QStringList directories() const
Returns a list of paths to directories that are being watched.
The QDateTime class provides date and time functions.
Definition: qdatetime.h:216
void start(Priority=InheritPriority)
Begins execution of the thread by calling run().
QObject * parent() const
Returns a pointer to the parent object.
Definition: qobject.h:273
bool wait(unsigned long time=ULONG_MAX)
Blocks the thread until either of these conditions is met:
int exec()
Enters the event loop and waits until exit() is called, returning the value that was passed to exit()...
Definition: qthread.cpp:551
The QHash::iterator class provides an STL-style non-const iterator for QHash and QMultiHash.
Definition: qhash.h:330
QString objectName() const
uint groupId() const
Returns the id of the group the file belongs to.
Definition: qfileinfo.cpp:1183
virtual QStringList addPaths(const QStringList &paths, QStringList *files, QStringList *directories)=0
static QKqueueFileSystemWatcherEngine * create()
QObject * parent
Definition: qobject.h:92
QStringList removePaths(const QStringList &paths, QStringList *files, QStringList *directories)
void addPath(const QString &file)
Adds path to the file system watcher if path exists.
static const MacVersion MacintoshVersion
the version of the Macintosh operating system on which the application is run (Mac only)...
Definition: qglobal.h:1646
The QTimer class provides repetitive and single-shot timers.
Definition: qtimer.h:56
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string ends with s; otherwise returns false.
Definition: qstring.cpp:3796
The QFileInfo class provides system-independent file information.
Definition: qfileinfo.h:60
void start(int msec)
Starts or restarts the timer with a timeout interval of msec milliseconds.
Definition: qtimer.cpp:249
#define qPrintable(string)
Definition: qglobal.h:1750
#define Q_UNUSED(x)
Indicates to the compiler that the parameter with the specified name is not used in the body of a fun...
Definition: qglobal.h:1729
QStringList entryList(Filters filters=NoFilter, SortFlags sort=NoSort) const
Returns a list of the names of all the files and directories in the directory, ordered according to t...
Definition: qdir.cpp:1290
The QLatin1Char class provides an 8-bit ASCII/Latin-1 character.
Definition: qchar.h:55
int removeAll(const T &t)
Removes all occurrences of value in the list and returns the number of entries removed.
Definition: qlist.h:770
bool operator!=(const QFileInfo &fileInfo) const
QDateTime lastModified() const
Returns the date and time when the file was last modified.
Definition: qfileinfo.cpp:1296