QStringList QFSEventsFileSystemWatcherEngine::addPaths(const QStringList &paths,
                                                       QStringList *files,
                                                       QStringList *directories)
{
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
    stop();
    wait();
    QMutexLocker locker(&mutex);
    QStringList failedToAdd;
    // if we have a running FSStreamEvent, we have to kill it, we'll re-add the stream soon.
    FSEventStreamEventId idToCheck;
    if (fsStream) {
        idToCheck = FSEventStreamGetLatestEventId(fsStream);
        cleanupFSStream(fsStream);
    } else {
        idToCheck = kFSEventStreamEventIdSinceNow;
    }

    // Brain-dead approach, but works. FSEvents actually can already read sub-trees, but since it's
    // work to figure out if we are doing a double register, we just register it twice as FSEvents
    // seems smart enough to only deliver one event. We also duplicate directory entries in here
    // (e.g., if you watch five files in the same directory, you get that directory included in the
    // array 5 times). This stupidity also makes remove work correctly though. I'll freely admit
    // that we could make this a bit smarter. If you do, check the auto-tests, they should catch at
    // least a couple of the issues.
    QCFType<CFMutableArrayRef> tmpArray = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
    for (int i = 0; i < paths.size(); ++i) {
        const QString &path = paths.at(i);

        QFileInfo fileInfo(path);
        if (!fileInfo.exists()) {
            failedToAdd.append(path);
            continue;
        }

        if (fileInfo.isDir()) {
            if (directories->contains(path)) {
                failedToAdd.append(path);
                continue;
            } else {
                directories->append(path);
                // Full file path for dirs.
                QCFString cfpath(createFSStreamPath(fileInfo.canonicalFilePath()));
                addPathToHash(dirPathInfoHash, cfpath, fileInfo, path);
                CFArrayAppendValue(tmpArray, cfpath);
            }
        } else {
            if (files->contains(path)) {
                failedToAdd.append(path);
                continue;
            } else {
                // Just the absolute path (minus it's filename) for files.
                QCFString cfpath(createFSStreamPath(fileInfo.canonicalPath()));
                files->append(path);
                addPathToHash(filePathInfoHash, cfpath, fileInfo, path);
                CFArrayAppendValue(tmpArray, cfpath);
            }
        }
    }

    if (!pathsToWatch && failedToAdd.size() == paths.size()) {
        return failedToAdd;
    }

    if (CFArrayGetCount(tmpArray) > 0) {
        if (pathsToWatch) {
            CFArrayAppendArray(tmpArray, pathsToWatch, CFRangeMake(0, CFArrayGetCount(pathsToWatch)));
            CFRelease(pathsToWatch);
        }
        pathsToWatch = CFArrayCreateCopy(kCFAllocatorDefault, tmpArray);
    }

    FSEventStreamContext context = { 0, this, 0, 0, 0 };
    fsStream = FSEventStreamCreate(kCFAllocatorDefault,
                                   QFSEventsFileSystemWatcherEngine::fseventsCallback,
                                   &context, pathsToWatch,
                                   idToCheck, Latency, QtFSEventFlags);
    warmUpFSEvents();

    return failedToAdd;
#else
    Q_UNUSED(paths);
    Q_UNUSED(files);
    Q_UNUSED(directories);
    return QStringList();
#endif
}
QStringList QFSEventsFileSystemWatcherEngine::removePaths(const QStringList &paths,
                                                          QStringList *files,
                                                          QStringList *directories)
{
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 && !defined(Q_OS_IOS)
    stop();
    wait();
    QMutexLocker locker(&mutex);
    // short circuit for smarties that call remove before add and we have nothing.
    if (pathsToWatch == 0)
        return paths;
    QStringList failedToRemove;
    // if we have a running FSStreamEvent, we have to stop it, we'll re-add the stream soon.
    FSEventStreamEventId idToCheck;
    if (fsStream) {
        idToCheck = FSEventStreamGetLatestEventId(fsStream);
        cleanupFSStream(fsStream);
        fsStream = 0;
    } else {
        idToCheck = kFSEventStreamEventIdSinceNow;
    }

    CFIndex itemCount = CFArrayGetCount(pathsToWatch);
    QCFType<CFMutableArrayRef> tmpArray = CFArrayCreateMutableCopy(kCFAllocatorDefault, itemCount,
                                                                   pathsToWatch);
    CFRelease(pathsToWatch);
    pathsToWatch = 0;
    for (int i = 0; i < paths.size(); ++i) {
        // Get the itemCount at the beginning to avoid any overruns during the iteration.
        itemCount = CFArrayGetCount(tmpArray);
        const QString &path = paths.at(i);
        QFileInfo fi(path);
        QCFString cfpath(createFSStreamPath(fi.canonicalPath()));

        CFIndex index = CFArrayGetFirstIndexOfValue(tmpArray, CFRangeMake(0, itemCount), cfpath);
        if (index != -1) {
            CFArrayRemoveValueAtIndex(tmpArray, index);
            files->removeAll(path);
            removePathFromHash(filePathInfoHash, cfpath, path);
        } else {
            // Could be a directory we are watching instead.
            QCFString cfdirpath(createFSStreamPath(fi.canonicalFilePath()));
            index = CFArrayGetFirstIndexOfValue(tmpArray, CFRangeMake(0, itemCount), cfdirpath);
            if (index != -1) {
                CFArrayRemoveValueAtIndex(tmpArray, index);
                directories->removeAll(path);
                removePathFromHash(dirPathInfoHash, cfpath, path);
            } else {
                failedToRemove.append(path);
            }
        }
    }
    itemCount = CFArrayGetCount(tmpArray);
    if (itemCount != 0) {
        pathsToWatch = CFArrayCreateCopy(kCFAllocatorDefault, tmpArray);

        FSEventStreamContext context = { 0, this, 0, 0, 0 };
        fsStream = FSEventStreamCreate(kCFAllocatorDefault,
                                       QFSEventsFileSystemWatcherEngine::fseventsCallback,
                                       &context, pathsToWatch, idToCheck, Latency, QtFSEventFlags);
        warmUpFSEvents();
    }
    return failedToRemove;
#else
    Q_UNUSED(paths);
    Q_UNUSED(files);
    Q_UNUSED(directories);
    return QStringList();
#endif
}