<commit_before>/****************************************************************************
**
** Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
** Contact:  Nokia Corporation (<EMAIL>)**
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met:  http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain
** additional rights. These rights are described in the Nokia Qt LGPL
** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
** package.
**
** If you have questions regarding the use of this file, please
** contact Nokia at http://www.qtsoftware.com/contact.
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qvaluespace.h"
#include "qvaluespaceobject.h"

#include <QSet>
#include <QStringList>
#include <QEventLoop>
#include <QTimer>

#include <QDebug>

#include <windows.h>

class RegistryLayer : public QAbstractValueSpaceLayer
{
    Q_OBJECT

public:
    RegistryLayer();
    ~RegistryLayer();

    /* Common functions */
    virtual QString name();
    virtual bool startup(Type t);
    virtual bool restart();
    virtual void shutdown();
    virtual QUuid id();
    virtual unsigned int order();
    virtual bool value(HANDLE handle, QVariant *data);
    virtual bool value(HANDLE handle, const QByteArray &subPath, QVariant *data);
    virtual QSet<QByteArray> children(HANDLE handle);
    virtual HANDLE item(HANDLE parent, const QByteArray &path);
    virtual void setProperty(HANDLE handle, Properties);
    virtual void remHandle(HANDLE);
    virtual bool syncChanges();

    void removeWatches(QValueSpaceObject *creator, HANDLE parent);

    /* QValueSpaceItem functions */
    bool requestSetValue(HANDLE handle, const QVariant &data);
    bool requestSetValue(HANDLE handle, const QByteArray &path, const QVariant &data);
    bool requestRemoveValue(HANDLE handle, const QByteArray &path = QByteArray());

    /* QValueSpaceObject functions */
    bool setValue(QValueSpaceObject *creator, HANDLE handle, const QVariant &data);
    bool setValue(QValueSpaceObject *creator, HANDLE handle, const QByteArray &path, const QVariant &data);
    bool removeValue(QValueSpaceObject *creator, HANDLE handle, const QByteArray &subPath);
    bool removeSubTree(QValueSpaceObject *creator, HANDLE parent);

    /* Private implementation functions */
    void emitHandleChanged(HKEY key);

    static RegistryLayer *instance();

private:
    void openRegistryKey(HANDLE handle);
    void createRegistryKey(HANDLE handle);
    bool removeRegistryValue(HANDLE handle, const QByteArray &path);

    QHash<QByteArray, HANDLE> handles;
    QList<HANDLE> valueHandles;
    QMap<HANDLE, int> refCount;

    QMap<HANDLE, HKEY> hKeys;
    QMap<HKEY, QPair<::HANDLE, ::HANDLE> > waitHandles;

    QMap<QValueSpaceObject *, QList<QByteArray> > creators;
};

#include <registrylayer_win.moc>

Q_GLOBAL_STATIC(RegistryLayer, registryLayer);
QVALUESPACE_AUTO_INSTALL_LAYER(RegistryLayer);

void CALLBACK qRegistryLayerCallback(PVOID lpParameter, BOOLEAN timedOut)
{
    registryLayer()->emitHandleChanged(reinterpret_cast<HKEY>(lpParameter));
}

static QString qConvertPath(const QByteArray &path)
{
    QString fixedPath = QString::fromUtf8(path.constData(), path.length());

    while (fixedPath.endsWith(QChar('/')))
        fixedPath.chop(1);

    fixedPath.replace(QChar('/'), QChar('\\'));

    return fixedPath;
}

RegistryLayer::RegistryLayer()
{
}

RegistryLayer::~RegistryLayer()
{
    while (!valueHandles.isEmpty())
        remHandle(valueHandles.takeFirst());

    while (!handles.isEmpty())
        remHandle(*handles.begin());
}

QString RegistryLayer::name()
{
    return QLatin1String("Registry Layer");
}

bool RegistryLayer::startup(Type t)
{
    // connect to registry?

    return true;
}

bool RegistryLayer::restart()
{
    // reconnect to registry?

    return true;
}

void RegistryLayer::shutdown()
{
}

QUuid RegistryLayer::id()
{
    return QUuid();
}

unsigned int RegistryLayer::order()
{
    return 0;
}

bool RegistryLayer::value(HANDLE handle, QVariant *data)
{
    return value(InvalidHandle, handles.key(handle), data);
}

bool RegistryLayer::value(HANDLE handle, const QByteArray &subPath, QVariant *data)
{
    if (handle != InvalidHandle && !handles.values().contains(handle))
        return false;

    QByteArray path(subPath);
    while (path.endsWith('/'))
        path.chop(1);
    if (handle != InvalidHandle)
        while (path.startsWith('/'))
            path = path.mid(1);

    int index = path.lastIndexOf('/', -1);

    bool createdHandle = false;

    QString value;
    if (index == -1) {
        value = QString::fromUtf8(path.constData(), path.length());
    } else {
        // want a value that is in a sub path under handle
        value = QString::fromUtf8(path.constData() + (index + 1), path.length() - (index + 1));
        path.truncate(index);

        handle = item(handle, path);
        createdHandle = true;
    }

    openRegistryKey(handle);
    if (!hKeys.contains(handle)) {
        if (createdHandle)
            remHandle(handle);

        return false;
    }

    HKEY key = hKeys.value(handle);

    DWORD regSize = 0;
    long result = RegQueryValueEx(key, value.utf16(), 0, 0, 0, &regSize);
    if (result == ERROR_FILE_NOT_FOUND) {
        *data = QVariant();
        if (createdHandle)
            remHandle(handle);
        return false;
    } else if (result != ERROR_SUCCESS) {
        qDebug() << "RegQueryValueEx failed with error" << result;
        if (createdHandle)
            remHandle(handle);
        return false;
    }

    BYTE *regData = new BYTE[regSize];
    DWORD regType;

    result = RegQueryValueEx(key, value.utf16(), 0, &regType, regData, &regSize);
    if (result != ERROR_SUCCESS) {
        qDebug() << "real RegQueryValueEx failed with error" << result;
        if (createdHandle)
            remHandle(handle);
        return false;
    }

    switch (regType) {
    case REG_DWORD:
        *data = qVariantFromValue(*reinterpret_cast<int *>(regData));
        break;
    case REG_QWORD:
        *data = qVariantFromValue(*reinterpret_cast<qint64 *>(regData));
        break;
    case REG_SZ:
        *data = qVariantFromValue(QString::fromWCharArray(reinterpret_cast<wchar_t *>(regData)));
        break;
    case REG_MULTI_SZ: {
        QStringList list;
        wchar_t *temp = reinterpret_cast<wchar_t *>(regData);
        while (*temp != L'\0') {
            QString string = QString::fromWCharArray(temp);
            list << string;
            temp += string.length() + 1;
        }
        *data = qVariantFromValue(list);
        break;
    }
    case REG_BINARY:
        *data = qVariantFromValue(QByteArray(reinterpret_cast<char *>(regData), regSize));
        break;
    case REG_NONE: {
        QDataStream stream(QByteArray::fromRawData(reinterpret_cast<char *>(regData), regSize));
        stream >> *data;
        break;
    }
    default:
        qDebug() << "Unknown REG type" << regType;
        delete[] regData;
        if (createdHandle)
            remHandle(handle);
        return false;
        break;
    }

    delete[] regData;

    if (createdHandle)
        remHandle(handle);

    return true;
}

#define MAX_KEY_LENGTH 255
#define MAX_NAME_LENGTH 16383
QSet<QByteArray> RegistryLayer::children(HANDLE handle)
{
    QSet<QByteArray> foundChildren;

    openRegistryKey(handle);
    if (!hKeys.contains(handle))
        return foundChildren;

    HKEY key = hKeys.value(handle);
    int i = 0;
    long result;
    do {
        TCHAR subKey[MAX_KEY_LENGTH];
        DWORD subKeySize = MAX_KEY_LENGTH;

        result = RegEnumKeyEx(key, i, subKey, &subKeySize, 0, 0, 0, 0);
        if (result == ERROR_NO_MORE_ITEMS)
            break;

        foundChildren << QString::fromWCharArray(subKey, subKeySize).toUtf8();
        ++i;
    } while (result == ERROR_SUCCESS);

    i = 0;
    do {
        TCHAR valueName[MAX_NAME_LENGTH];
        DWORD valueNameSize = MAX_NAME_LENGTH;

        result = RegEnumValue(key, i, valueName, &valueNameSize, 0, 0, 0, 0);
        if (result == ERROR_NO_MORE_ITEMS)
            break;

        foundChildren << QString::fromWCharArray(valueName, valueNameSize).toUtf8();
        ++i;
    } while (result == ERROR_SUCCESS);

    return foundChildren;
}

QAbstractValueSpaceLayer::HANDLE RegistryLayer::item(HANDLE parent, const QByteArray &path)
{
    QByteArray fullPath;

    // Fail on invalid path.
    if (path.isEmpty() || path.contains("//"))
        return InvalidHandle;

    if (parent == InvalidHandle) {
        fullPath = path;
    } else {
        QByteArray parentPath = handles.key(parent);

        if (parentPath.endsWith('/') && path.startsWith('/'))
            fullPath = parentPath + path.mid(1);
        else if (!parentPath.endsWith('/') && !path.startsWith('/'))
            fullPath = parentPath + '/' + path;
        else
            fullPath = parentPath + path;
    }

    if (handles.contains(fullPath)) {
        HANDLE handle = handles.value(fullPath);
        ++refCount[handle];
        return handle;
    }

    // Create random handle for path
    HANDLE handle = qrand();

    QList<HANDLE> allocatedHandles = handles.values();
    while (allocatedHandles.contains(handle) && handle != InvalidHandle)
        handle = qrand();

    if (handle == InvalidHandle)
        return InvalidHandle;

    handles.insert(fullPath, handle);
    refCount.insert(handle, 1);

    return handle;
}

void RegistryLayer::setProperty(HANDLE handle, Properties properties)
{
    if (!handles.values().contains(handle))
        return;

    if (properties & QAbstractValueSpaceLayer::Publish) {
        // Enable change notification for handle
        openRegistryKey(handle);
        if (!hKeys.contains(handle))
            return;

        HKEY key = hKeys.value(handle);

        if (waitHandles.contains(key))
            return;

        ::HANDLE event = CreateEvent(0, false, false, 0);
        if (event == 0) {
            qDebug() << "CreateEvent failed with error" << GetLastError();
            return;
        }

        DWORD filter = REG_NOTIFY_CHANGE_LAST_SET;
        // REG_NOTIFY_CHANGE_NAME REG_NOTIFY_CHANGE_ATTRIBUTES REG_NOTIFY_CHANGE_LAST_SET
        long result = RegNotifyChangeKeyValue(key, true, filter, event, true);

        if (result != ERROR_SUCCESS) {
            qDebug() << "RegNotifyChangeKeyValue failed with error" << result;
            return;
        }

        ::HANDLE waitHandle;
        bool ret = RegisterWaitForSingleObject(&waitHandle, event, &qRegistryLayerCallback, key, INFINITE, WT_EXECUTEDEFAULT);
        if (!ret) {
            qDebug() << "RegisterWaitForSingleObject failed with error" << GetLastError();
            return;
        }

        waitHandles.insert(key, QPair<::HANDLE, ::HANDLE>(event, waitHandle));
    }
    if (!(properties & QAbstractValueSpaceLayer::Publish)) {
        // Disable change notification for handle
        if (!hKeys.contains(handle))
            return;

        HKEY key = hKeys.value(handle);

        QPair<::HANDLE, ::HANDLE> wait = waitHandles.take(key);
        ::HANDLE event = wait.first;
        ::HANDLE waitHandle = wait.second;

        UnregisterWait(waitHandle);
        CloseHandle(event);
    }
}

void RegistryLayer::remHandle(HANDLE handle)
{
    if (!handles.values().contains(handle))
        return;

    if (--refCount[handle])
        return;

    refCount.remove(handle);
    handles.remove(handles.key(handle));
    valueHandles.removeOne(handle);

    HKEY key = hKeys.take(handle);

    // Check if other handles are using this registry key.
    if (!hKeys.keys(key).isEmpty())
        return;

    QPair<::HANDLE, ::HANDLE> wait = waitHandles.take(key);

    UnregisterWait(wait.second);
    CloseHandle(wait.first);

    RegCloseKey(key);
}

static LSTATUS qRegDeleteTree(HKEY hKey, LPCTSTR lpSubKey)
{
    HKEY key;
    long result = RegOpenKeyEx(hKey, lpSubKey, 0, KEY_ALL_ACCESS, &key);
    if (result != ERROR_SUCCESS)
        return result;

    do {
        TCHAR subKey[MAX_KEY_LENGTH];
        DWORD subKeySize = MAX_KEY_LENGTH;

        long result = RegEnumKeyEx(key, 0, subKey, &subKeySize, 0, 0, 0, 0);
        if (result == ERROR_NO_MORE_ITEMS)
            break;

        result = qRegDeleteTree(key, subKey);
    } while (result == ERROR_SUCCESS);

    RegCloseKey(key);

    if (result != ERROR_NO_MORE_ITEMS && result != ERROR_SUCCESS)
        return result;

    return RegDeleteKey(hKey, lpSubKey);
}

bool RegistryLayer::removeRegistryValue(HANDLE handle, const QByteArray &subPath)
{
    if (handle != InvalidHandle && !handles.values().contains(handle))
        return false;

    QByteArray path(subPath);
    while (path.endsWith('/'))
        path.chop(1);

    int index = path.lastIndexOf('/', -1);

    bool createdHandle = false;

    QString value;
    if (index == -1) {
        value = path;
    } else {
        // want a value that is in a sub path under handle
        value = QString::fromUtf8(path.constData() + (index + 1), path.length() - (index + 1));
        path.truncate(index);
        if (path.isEmpty())
            path.append('/');

        handle = item(handle, path);
        createdHandle = true;
    }

    openRegistryKey(handle);
    if (!hKeys.contains(handle)) {
        if (createdHandle)
            remHandle(handle);

        return false;
    }

    HKEY key = hKeys.value(handle);

    long result = RegDeleteValue(key, value.utf16());
    if (result == ERROR_FILE_NOT_FOUND) {
        result = qRegDeleteTree(key, value.utf16());
        if (result != ERROR_SUCCESS)
            qDebug() << "RegDeleteTree failed with error" << result;
    } else if (result != ERROR_SUCCESS) {
        qDebug() << "RegDeleteValue failed with error" << result;
    }

    if (createdHandle)
        remHandle(handle);

    return result == ERROR_SUCCESS;
}<commit_msg>Close registry keys when they are deleted.<commit_after>/****************************************************************************
**
** Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
** Contact:  Nokia Corporation (<EMAIL>)**
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met:  http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain
** additional rights. These rights are described in the Nokia Qt LGPL
** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
** package.
**
** If you have questions regarding the use of this file, please
** contact Nokia at http://www.qtsoftware.com/contact.
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qvaluespace.h"
#include "qvaluespaceobject.h"

#include <QSet>
#include <QStringList>
#include <QEventLoop>
#include <QTimer>

#include <QDebug>

#include <windows.h>

class RegistryLayer : public QAbstractValueSpaceLayer
{
    Q_OBJECT

public:
    RegistryLayer();
    ~RegistryLayer();

    /* Common functions */
    virtual QString name();
    virtual bool startup(Type t);
    virtual bool restart();
    virtual void shutdown();
    virtual QUuid id();
    virtual unsigned int order();
    virtual bool value(HANDLE handle, QVariant *data);
    virtual bool value(HANDLE handle, const QByteArray &subPath, QVariant *data);
    virtual QSet<QByteArray> children(HANDLE handle);
    virtual HANDLE item(HANDLE parent, const QByteArray &path);
    virtual void setProperty(HANDLE handle, Properties);
    virtual void remHandle(HANDLE);
    virtual bool syncChanges();

    void removeWatches(QValueSpaceObject *creator, HANDLE parent);

    /* QValueSpaceItem functions */
    bool requestSetValue(HANDLE handle, const QVariant &data);
    bool requestSetValue(HANDLE handle, const QByteArray &path, const QVariant &data);
    bool requestRemoveValue(HANDLE handle, const QByteArray &path = QByteArray());

    /* QValueSpaceObject functions */
    bool setValue(QValueSpaceObject *creator, HANDLE handle, const QVariant &data);
    bool setValue(QValueSpaceObject *creator, HANDLE handle, const QByteArray &path, const QVariant &data);
    bool removeValue(QValueSpaceObject *creator, HANDLE handle, const QByteArray &subPath);
    bool removeSubTree(QValueSpaceObject *creator, HANDLE parent);

    /* Private implementation functions */
    void emitHandleChanged(HKEY key);

    static RegistryLayer *instance();

private:
    void openRegistryKey(HANDLE handle);
    void createRegistryKey(HANDLE handle);
    bool removeRegistryValue(HANDLE handle, const QByteArray &path);
    void closeRegistryKey(HANDLE handle);

    QHash<QByteArray, HANDLE> handles;
    QList<HANDLE> valueHandles;
    QMap<HANDLE, int> refCount;

    QMap<HANDLE, HKEY> hKeys;
    QMap<HKEY, QPair<::HANDLE, ::HANDLE> > waitHandles;

    QMap<QValueSpaceObject *, QList<QByteArray> > creators;
};

#include <registrylayer_win.moc>

Q_GLOBAL_STATIC(RegistryLayer, registryLayer);
QVALUESPACE_AUTO_INSTALL_LAYER(RegistryLayer);

void CALLBACK qRegistryLayerCallback(PVOID lpParameter, BOOLEAN timedOut)
{
    registryLayer()->emitHandleChanged(reinterpret_cast<HKEY>(lpParameter));
}

static QString qConvertPath(const QByteArray &path)
{
    QString fixedPath = QString::fromUtf8(path.constData(), path.length());

    while (fixedPath.endsWith(QChar('/')))
        fixedPath.chop(1);

    fixedPath.replace(QChar('/'), QChar('\\'));

    return fixedPath;
}

RegistryLayer::RegistryLayer()
{
}

RegistryLayer::~RegistryLayer()
{
    while (!valueHandles.isEmpty())
        remHandle(valueHandles.takeFirst());

    while (!handles.isEmpty())
        remHandle(*handles.begin());
}

QString RegistryLayer::name()
{
    return QLatin1String("Registry Layer");
}

bool RegistryLayer::startup(Type t)
{
    // connect to registry?

    return true;
}

bool RegistryLayer::restart()
{
    // reconnect to registry?

    return true;
}

void RegistryLayer::shutdown()
{
}

QUuid RegistryLayer::id()
{
    return QUuid();
}

unsigned int RegistryLayer::order()
{
    return 0;
}

bool RegistryLayer::value(HANDLE handle, QVariant *data)
{
    return value(InvalidHandle, handles.key(handle), data);
}

bool RegistryLayer::value(HANDLE handle, const QByteArray &subPath, QVariant *data)
{
    if (handle != InvalidHandle && !handles.values().contains(handle))
        return false;

    QByteArray path(subPath);
    while (path.endsWith('/'))
        path.chop(1);
    if (handle != InvalidHandle)
        while (path.startsWith('/'))
            path = path.mid(1);

    int index = path.lastIndexOf('/', -1);

    bool createdHandle = false;

    QString value;
    if (index == -1) {
        value = QString::fromUtf8(path.constData(), path.length());
    } else {
        // want a value that is in a sub path under handle
        value = QString::fromUtf8(path.constData() + (index + 1), path.length() - (index + 1));
        path.truncate(index);

        handle = item(handle, path);
        createdHandle = true;
    }

    openRegistryKey(handle);
    if (!hKeys.contains(handle)) {
        if (createdHandle)
            remHandle(handle);

        return false;
    }

    HKEY key = hKeys.value(handle);

    DWORD regSize = 0;
    long result = RegQueryValueEx(key, value.utf16(), 0, 0, 0, &regSize);
    if (result == ERROR_FILE_NOT_FOUND) {
        *data = QVariant();
        if (createdHandle)
            remHandle(handle);
        return false;
    } else if (result != ERROR_SUCCESS) {
        qDebug() << "RegQueryValueEx failed with error" << result;
        if (createdHandle)
            remHandle(handle);
        return false;
    }

    BYTE *regData = new BYTE[regSize];
    DWORD regType;

    result = RegQueryValueEx(key, value.utf16(), 0, &regType, regData, &regSize);
    if (result != ERROR_SUCCESS) {
        qDebug() << "real RegQueryValueEx failed with error" << result;
        if (createdHandle)
            remHandle(handle);
        return false;
    }

    switch (regType) {
    case REG_DWORD:
        *data = qVariantFromValue(*reinterpret_cast<int *>(regData));
        break;
    case REG_QWORD:
        *data = qVariantFromValue(*reinterpret_cast<qint64 *>(regData));
        break;
    case REG_SZ:
        *data = qVariantFromValue(QString::fromWCharArray(reinterpret_cast<wchar_t *>(regData)));
        break;
    case REG_MULTI_SZ: {
        QStringList list;
        wchar_t *temp = reinterpret_cast<wchar_t *>(regData);
        while (*temp != L'\0') {
            QString string = QString::fromWCharArray(temp);
            list << string;
            temp += string.length() + 1;
        }
        *data = qVariantFromValue(list);
        break;
    }
    case REG_BINARY:
        *data = qVariantFromValue(QByteArray(reinterpret_cast<char *>(regData), regSize));
        break;
    case REG_NONE: {
        QDataStream stream(QByteArray::fromRawData(reinterpret_cast<char *>(regData), regSize));
        stream >> *data;
        break;
    }
    default:
        qDebug() << "Unknown REG type" << regType;
        delete[] regData;
        if (createdHandle)
            remHandle(handle);
        return false;
        break;
    }

    delete[] regData;

    if (createdHandle)
        remHandle(handle);

    return true;
}

#define MAX_KEY_LENGTH 255
#define MAX_NAME_LENGTH 16383
QSet<QByteArray> RegistryLayer::children(HANDLE handle)
{
    QSet<QByteArray> foundChildren;

    openRegistryKey(handle);
    if (!hKeys.contains(handle))
        return foundChildren;

    HKEY key = hKeys.value(handle);
    int i = 0;
    long result;
    do {
        TCHAR subKey[MAX_KEY_LENGTH];
        DWORD subKeySize = MAX_KEY_LENGTH;

        result = RegEnumKeyEx(key, i, subKey, &subKeySize, 0, 0, 0, 0);
        if (result == ERROR_NO_MORE_ITEMS)
            break;

        foundChildren << QString::fromWCharArray(subKey, subKeySize).toUtf8();
        ++i;
    } while (result == ERROR_SUCCESS);

    i = 0;
    do {
        TCHAR valueName[MAX_NAME_LENGTH];
        DWORD valueNameSize = MAX_NAME_LENGTH;

        result = RegEnumValue(key, i, valueName, &valueNameSize, 0, 0, 0, 0);
        if (result == ERROR_NO_MORE_ITEMS)
            break;

        foundChildren << QString::fromWCharArray(valueName, valueNameSize).toUtf8();
        ++i;
    } while (result == ERROR_SUCCESS);

    return foundChildren;
}

QAbstractValueSpaceLayer::HANDLE RegistryLayer::item(HANDLE parent, const QByteArray &path)
{
    QByteArray fullPath;

    // Fail on invalid path.
    if (path.isEmpty() || path.contains("//"))
        return InvalidHandle;

    if (parent == InvalidHandle) {
        fullPath = path;
    } else {
        QByteArray parentPath = handles.key(parent);

        if (parentPath.endsWith('/') && path.startsWith('/'))
            fullPath = parentPath + path.mid(1);
        else if (!parentPath.endsWith('/') && !path.startsWith('/'))
            fullPath = parentPath + '/' + path;
        else
            fullPath = parentPath + path;
    }

    if (handles.contains(fullPath)) {
        HANDLE handle = handles.value(fullPath);
        ++refCount[handle];
        return handle;
    }

    // Create random handle for path
    HANDLE handle = qrand();

    QList<HANDLE> allocatedHandles = handles.values();
    while (allocatedHandles.contains(handle) && handle != InvalidHandle)
        handle = qrand();

    if (handle == InvalidHandle)
        return InvalidHandle;

    handles.insert(fullPath, handle);
    refCount.insert(handle, 1);

    return handle;
}

void RegistryLayer::setProperty(HANDLE handle, Properties properties)
{
    if (!handles.values().contains(handle))
        return;

    if (properties & QAbstractValueSpaceLayer::Publish) {
        // Enable change notification for handle
        openRegistryKey(handle);
        if (!hKeys.contains(handle))
            return;

        HKEY key = hKeys.value(handle);

        if (waitHandles.contains(key))
            return;

        ::HANDLE event = CreateEvent(0, false, false, 0);
        if (event == 0) {
            qDebug() << "CreateEvent failed with error" << GetLastError();
            return;
        }

        DWORD filter = REG_NOTIFY_CHANGE_LAST_SET;
        // REG_NOTIFY_CHANGE_NAME REG_NOTIFY_CHANGE_ATTRIBUTES REG_NOTIFY_CHANGE_LAST_SET
        long result = RegNotifyChangeKeyValue(key, true, filter, event, true);

        if (result != ERROR_SUCCESS) {
            qDebug() << "RegNotifyChangeKeyValue failed with error" << result;
            return;
        }

        ::HANDLE waitHandle;
        bool ret = RegisterWaitForSingleObject(&waitHandle, event, &qRegistryLayerCallback, key, INFINITE, WT_EXECUTEDEFAULT);
        if (!ret) {
            qDebug() << "RegisterWaitForSingleObject failed with error" << GetLastError();
            return;
        }

        waitHandles.insert(key, QPair<::HANDLE, ::HANDLE>(event, waitHandle));
    }
    if (!(properties & QAbstractValueSpaceLayer::Publish)) {
        // Disable change notification for handle
        if (!hKeys.contains(handle))
            return;

        HKEY key = hKeys.value(handle);

        QPair<::HANDLE, ::HANDLE> wait = waitHandles.take(key);
        ::HANDLE event = wait.first;
        ::HANDLE waitHandle = wait.second;

        UnregisterWait(waitHandle);
        CloseHandle(event);
    }
}

void RegistryLayer::remHandle(HANDLE handle)
{
    if (!handles.values().contains(handle))
        return;

    if (--refCount[handle])
        return;

    refCount.remove(handle);
    handles.remove(handles.key(handle));
    valueHandles.removeOne(handle);

    closeRegistryKey(handle);
}

void RegistryLayer::closeRegistryKey(HANDLE handle)
{
    if (!hKeys.contains(handle))
        return;

    HKEY key = hKeys.take(handle);

    // Check if other handles are using this registry key.
    if (!hKeys.keys(key).isEmpty())
        return;

    QPair<::HANDLE, ::HANDLE> wait = waitHandles.take(key);

    UnregisterWait(wait.second);
    CloseHandle(wait.first);

    RegCloseKey(key);
}

static LSTATUS qRegDeleteTree(HKEY hKey, LPCTSTR lpSubKey)
{
    HKEY key;
    long result = RegOpenKeyEx(hKey, lpSubKey, 0, KEY_ALL_ACCESS, &key);
    if (result != ERROR_SUCCESS)
        return result;

    do {
        TCHAR subKey[MAX_KEY_LENGTH];
        DWORD subKeySize = MAX_KEY_LENGTH;

        long result = RegEnumKeyEx(key, 0, subKey, &subKeySize, 0, 0, 0, 0);
        if (result == ERROR_NO_MORE_ITEMS)
            break;

        result = qRegDeleteTree(key, subKey);
    } while (result == ERROR_SUCCESS);

    RegCloseKey(key);

    if (result != ERROR_NO_MORE_ITEMS && result != ERROR_SUCCESS)
        return result;

    return RegDeleteKey(hKey, lpSubKey);
}

bool RegistryLayer::removeRegistryValue(HANDLE handle, const QByteArray &subPath)
{
    if (handle != InvalidHandle && !handles.values().contains(handle))
        return false;

    QByteArray path(subPath);
    while (path.endsWith('/'))
        path.chop(1);

    int index = path.lastIndexOf('/', -1);

    bool createdHandle = false;

    QString value;
    if (index == -1) {
        value = path;
    } else {
        // want a value that is in a sub path under handle
        value = QString::fromUtf8(path.constData() + (index + 1), path.length() - (index + 1));
        path.truncate(index);
        if (path.isEmpty())
            path.append('/');

        handle = item(handle, path);
        createdHandle = true;
    }

    openRegistryKey(handle);
    if (!hKeys.contains(handle)) {
        if (createdHandle)
            remHandle(handle);

        return false;
    }

    HKEY key = hKeys.value(handle);

    long result = RegDeleteValue(key, value.utf16());
    if (result == ERROR_FILE_NOT_FOUND) {
        result = qRegDeleteTree(key, value.utf16());
        if (result == ERROR_SUCCESS) {
            const QByteArray rootPath = handles.key(handle);

            QList<QByteArray> paths = handles.keys();
            while (!paths.isEmpty()) {
                QByteArray p = paths.takeFirst();

                if (p.startsWith(rootPath))
                    closeRegistryKey(handles.value(p));
            }
        }
        if (result != ERROR_SUCCESS)
            qDebug() << "RegDeleteTree failed with error" << result;
    } else if (result != ERROR_SUCCESS) {
        qDebug() << "RegDeleteValue failed with error" << result;
    }

    if (createdHandle)
        remHandle(handle);

    return result == ERROR_SUCCESS;
}
{'fraction_non_alphanumeric': 0.12718229462229194, 'fraction_numerical': 0.005474513469306005, 'mean_word_length': 3.0056157240272765, 'pattern_counts': {'":': 0, '<': 140, '<?xml version=': 0, '>': 70, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7032145', 'n_tokens_mistral': 9061, 'n_tokens_neox': 8671, 'n_words': 2276}