Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/configmgr/source/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 54 kB image not shown  

Quelle  dconf.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */


#include <sal/config.h>

#include <cassert>
#include <cstddef>
#include <cstring>
#include <forward_list>
#include <limits>
#include <vector>

#include <dconf/dconf.h>

#include <com/sun/star/uno/Sequence.hxx>
#include <o3tl/safeint.hxx>
#include <rtl/ustrbuf.hxx>
#include <sal/log.hxx>

#include "data.hxx"
#include "dconf.hxx"
#include "groupnode.hxx"
#include "localizedpropertynode.hxx"
#include "localizedvaluenode.hxx"
#include "nodemap.hxx"
#include "propertynode.hxx"
#include "setnode.hxx"

// component-data is encoded in dconf as follows:
//
// * The node hierarchy (starting at component nodes with names like
//   "org.openoffice.Setup") maps to dconf paths underneath
//   "/org/libreoffice/registry/".
//
// * Component, group, set, and localized property nodes map to dconf dirs,
//   while property and localized value nodes map to dconf keys.
//
// * The names of nodes that are not set elements are used directly as dconf
//   path segments.  (The syntax for node names is any non-empty sequences of
//   any Unicode scalar values except U+0000--0008, U+000B--000C, U+000E--001F,
//   U+002F SOLIDUS, and U+FFFE--FFFF.  TODO: "<aruiz> sberg, in general I think
//   it'd be nice if you used path separators instead of dots though, they have
//   meaning in dconf/gvdb world :-)"?)
//
// * The names of set element nodes are encoded as dconf path segments as
//   follows: each occurrence of U+0000 NULL is replace by the three characters
//   "\00", each occurrence of U+002F SOLIDUS is replaced by the three
//   characters "\2F", and each occurrence of U+005C REVERSE SOLIDUS is replaced
//   by the three characters "\5C".
//
// * Set elements (which must themselves be either sets or groups) map to
//   "indirection" dconf dirs as follows:
//
// ** The dir must contain a key named "op" of string type, with a value of
//    "fuse", "replace", or "remove".
//
// ** If "op" is "fuse" or "replace", the dir must contain exactly the following
//    further keys and dirs:
//
// *** The dir must contain a key named "template" of string type, containing
//     the full template name, encoded as follows: each occurrence of U+0000
//     NULL is replace by the three characters "\00" and each occurrence of
//     U+005C REVERSE SOLIDUS is replaced by the three characters "\5C".
//
// *** The dir must contain a dir named "content" that contains the set
//     element's (i.e., set or group node's) real content.
//
// ** If "op" is "remove", the dir must contain no further keys or dirs.
//
// * Property and localized property value "fuse" operations map to GVariant
//   instances as follows:
//
// ** Non-nillable boolean values map to GVariant boolean instances.
//
// ** Non-nillable short values map to GVariant int16 instances.
//
// ** Non-nillable int values map to GVariant int32 instances.
//
// ** Non-nillable long values map to GVariant int64 instances.
//
// ** Non-nillable double values map to GVariant double instances.
//
// ** Non-nillable string values map to GVariant string instances, with the
//    following encoding: each occurrence of U+0000 NULL is replace by the three
//    characters "\00" and each occurrence of U+005C REVERSE SOLIDUS is replaced
//    by the three characters "\5C".
//
// ** Non-nillable hexbinary values map to GVariant byte array instances.
//
// ** Non-nillable list values recursively map to GVariant array instances.
//
// ** Nillable values recursively map to GVariant maybe instances.
//
// * Property "remove" operations map to GVariant instances of empty tuple type.
//
// Finalization:  The component-update.dtd allows for finalization of
// oor:component-data, node, and prop elements, while dconf allows for locking
// of individual keys.  That does not match, but just mark the individual Node
// instances that correspond to individual dconf keys as finalized for
// non-writable dconf keys.
//
// TODO: support "mandatory" and "external"?

namespace configmgr::dconf {

namespace {

template<typename T> class GObjectHolder {
public:
    explicit GObjectHolder(T * object): object_(object) {}

    ~GObjectHolder() {
        if (object_ != nullptr) {
            g_object_unref(object_);
        }
    }

    T * get() const { return object_; }

private:
    GObjectHolder(GObjectHolder const &) = delete;
    GObjectHolder& operator =(GObjectHolder const &) = delete;

    T * object_;
};

class GVariantHolder {
public:
    explicit GVariantHolder(GVariant * variant = nullptr): variant_(variant) {}

    ~GVariantHolder() { unref(); }

    void reset(GVariant * variant) {
        unref();
        variant_ = variant;
    }

    void release() { variant_ = nullptr; }

    GVariant * get() const { return variant_; }

private:
    GVariantHolder(GVariantHolder const &) = delete;
    GVariantHolder& operator =(GVariantHolder const &) = delete;

    void unref() {
        if (variant_ != nullptr) {
            g_variant_unref(variant_);
        }
    }

    GVariant * variant_;
};

class GVariantTypeHolder {
public:
    explicit GVariantTypeHolder(GVariantType * type): type_(type) {}

    ~GVariantTypeHolder() {
        if (type_ != nullptr) {
            g_variant_type_free(type_);
        }
    }

    GVariantType * get() const { return type_; }

private:
    GVariantTypeHolder(GVariantTypeHolder const &) = delete;
    GVariantTypeHolder& operator =(GVariantTypeHolder const &) = delete;

    GVariantType * type_;
};

class StringArrayHolder {
public:
    explicit StringArrayHolder(gchar ** array): array_(array) {}

    ~StringArrayHolder() { g_strfreev(array_); }

    gchar ** get() const { return array_; }

private:
    StringArrayHolder(StringArrayHolder const &) = delete;
    StringArrayHolder& operator =(StringArrayHolder const &) = delete;

    gchar ** array_;
};

class ChangesetHolder {
public:
    explicit ChangesetHolder(DConfChangeset * changeset):
        changeset_(changeset)
    {}

    ~ChangesetHolder() {
        if (changeset_ != nullptr) {
            dconf_changeset_unref(changeset_);
        }
    }

    DConfChangeset * get() const { return changeset_; }

private:
    ChangesetHolder(ChangesetHolder const &) = delete;
    ChangesetHolder& operator =(ChangesetHolder const &) = delete;

    DConfChangeset * changeset_;
};

OString getRoot() {
    return "/org/libreoffice/registry"_ostr;
}

bool decode(OUString * string, bool slash) {
    for (sal_Int32 i = 0;; ++i) {
        i = string->indexOf('\\', i);
        if (i == -1) {
            return true;
        }
        if (string->match("00", i + 1)) {
            *string = string->replaceAt(i, 3, OUStringChar(u'\0'));
        } else if (slash && string->match("2F", i + 1)) {
            *string = string->replaceAt(i, 3, u"/");
        } else if (string->match("5C", i + 1)) {
            *string = string->replaceAt(i + 1, 2, u"");
        } else {
            SAL_WARN("configmgr.dconf""bad escape in " << *string);
            return false;
        }
    }
}

bool getBoolean(
    OString const & key, GVariantHolder const & variant, css::uno::Any * value)
{
    assert(value != nullptr);
    if (!g_variant_is_of_type(variant.get(), G_VARIANT_TYPE_BOOLEAN)) {
        SAL_WARN(
            "configmgr.dconf",
            "bad key " << key << " does not match boolean property");
        return false;
    }
    *value <<= bool(g_variant_get_boolean(variant.get()));
    return true;
}

bool getShort(
    OString const & key, GVariantHolder const & variant, css::uno::Any * value)
{
    assert(value != nullptr);
    if (!g_variant_is_of_type(variant.get(), G_VARIANT_TYPE_INT16)) {
        SAL_WARN(
            "configmgr.dconf",
            "bad key " << key << " does not match short property");
        return false;
    }
    *value <<= sal_Int16(g_variant_get_int16(variant.get()));
    return true;
}

bool getInt(
    OString const & key, GVariantHolder const & variant, css::uno::Any * value)
{
    assert(value != nullptr);
    if (!g_variant_is_of_type(variant.get(), G_VARIANT_TYPE_INT32)) {
        SAL_WARN(
            "configmgr.dconf",
            "bad key " << key << " does not match int property");
        return false;
    }
    *value <<= sal_Int32(g_variant_get_int32(variant.get()));
    return true;
}

bool getLong(
    OString const & key, GVariantHolder const & variant, css::uno::Any * value)
{
    assert(value != nullptr);
    if (!g_variant_is_of_type(variant.get(), G_VARIANT_TYPE_INT64)) {
        SAL_WARN(
            "configmgr.dconf",
            "bad key " << key << " does not match long property");
        return false;
    }
    *value <<= sal_Int64(g_variant_get_int64(variant.get()));
    return true;
}

bool getDouble(
    OString const & key, GVariantHolder const & variant, css::uno::Any * value)
{
    assert(value != nullptr);
    if (!g_variant_is_of_type(variant.get(), G_VARIANT_TYPE_DOUBLE)) {
        SAL_WARN(
            "configmgr.dconf",
            "bad key " << key << " does not match double property");
        return false;
    }
    *value <<= double(g_variant_get_double(variant.get()));
    return true;
}

bool getStringValue(
    OString const & key, GVariantHolder const & variant, OUString * value)
{
    assert(value != nullptr);
    if (!g_variant_is_of_type(variant.get(), G_VARIANT_TYPE_STRING)) {
        SAL_WARN(
            "configmgr.dconf",
            "bad key " << key << " does not match string property");
        return false;
    }
    gsize n;
    char const * p = g_variant_get_string(variant.get(), &n);
    if (n > o3tl::make_unsigned(
            std::numeric_limits<sal_Int32>::max()))
    {
        SAL_WARN("configmgr.dconf""too long string value for key " << key);
        return false;
    }
    if (!rtl_convertStringToUString(
            &value->pData, p, static_cast<sal_Int32>(n), RTL_TEXTENCODING_UTF8,
            (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
             | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
             | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
    {
        SAL_WARN("configmgr.dconf""non--UTF-8 string value for key " << key);
        return false;
    }
    return decode(value, false);
}

bool getString(
    OString const & key, GVariantHolder const & variant, css::uno::Any * value)
{
    assert(value != nullptr);
    OUString v;
    if (!getStringValue(key, variant, &v)) {
        return false;
    }
    *value <<= v;
    return true;
}

bool getHexbinaryValue(
    OString const & key, GVariantHolder const & variant,
    css::uno::Sequence<sal_Int8> * value)
{
    assert(value != nullptr);
    if (std::strcmp(g_variant_get_type_string(variant.get()), "ay") != 0) {
        SAL_WARN(
            "configmgr.dconf",
            "bad key " << key << " does not match hexbinary property");
        return false;
    }
    gsize n;
    gconstpointer p = g_variant_get_fixed_array(
        variant.get(), &n, sizeof (guchar));
    if (n > o3tl::make_unsigned(
            std::numeric_limits<sal_Int32>::max()))
    {
        SAL_WARN("configmgr.dconf""too long hexbinary value for key " << key);
        return false;
    }
    value->realloc(static_cast<sal_Int32>(n));
    static_assert(sizeof (sal_Int8) == sizeof (guchar), "size mismatch");
    std::memcpy(value->getArray(), p, n * sizeof (guchar));
        // assuming that n * sizeof (guchar) is small enough for std::size_t
    return true;
}

bool getHexbinary(
    OString const & key, GVariantHolder const & variant, css::uno::Any * value)
{
    assert(value != nullptr);
    css::uno::Sequence<sal_Int8> v;
    if (!getHexbinaryValue(key, variant, &v)) {
        return false;
    }
    *value <<= v;
    return true;
}

bool getBooleanList(
    OString const & key, GVariantHolder const & variant, css::uno::Any * value)
{
    assert(value != nullptr);
    if (std::strcmp(g_variant_get_type_string(variant.get()), "ab") != 0) {
        SAL_WARN(
            "configmgr.dconf",
            "bad key " << key << " does not match boolean list property");
        return false;
    }
    gsize n;
    gconstpointer p = g_variant_get_fixed_array(
        variant.get(), &n, sizeof (guchar));
    if (n > o3tl::make_unsigned(
            std::numeric_limits<sal_Int32>::max()))
    {
        SAL_WARN("configmgr.dconf""too long boolean list for key " << key);
        return false;
    }
    css::uno::Sequence<sal_Bool> v(static_cast<sal_Int32>(n));
    static_assert(sizeof (sal_Bool) == sizeof (guchar), "size mismatch");
    std::memcpy(v.getArray(), p, n * sizeof (guchar));
        // assuming that n * sizeof (guchar) is small enough for std::size_t
    *value <<= v;
    return true;
}

bool getShortList(
    OString const & key, GVariantHolder const & variant, css::uno::Any * value)
{
    assert(value != nullptr);
    if (std::strcmp(g_variant_get_type_string(variant.get()), "an") != 0) {
        SAL_WARN(
            "configmgr.dconf",
            "bad key " << key << " does not match short list property");
        return false;
    }
    gsize n;
    gconstpointer p = g_variant_get_fixed_array(
        variant.get(), &n, sizeof (gint16));
    if (n > o3tl::make_unsigned(
            std::numeric_limits<sal_Int32>::max()))
    {
        SAL_WARN("configmgr.dconf""too long short list for key " << key);
        return false;
    }
    css::uno::Sequence<sal_Int16> v(static_cast<sal_Int32>(n));
    static_assert(sizeof (sal_Int16) == sizeof (gint16), "size mismatch");
    std::memcpy(v.getArray(), p, n * sizeof (gint16));
        // assuming that n * sizeof (gint16) is small enough for std::size_t
    *value <<= v;
    return true;
}

bool getIntList(
    OString const & key, GVariantHolder const & variant, css::uno::Any * value)
{
    assert(value != nullptr);
    if (std::strcmp(g_variant_get_type_string(variant.get()), "ai") != 0) {
        SAL_WARN(
            "configmgr.dconf",
            "bad key " << key << " does not match int list property");
        return false;
    }
    gsize n;
    gconstpointer p = g_variant_get_fixed_array(
        variant.get(), &n, sizeof (gint32));
    if (n > o3tl::make_unsigned(
            std::numeric_limits<sal_Int32>::max()))
    {
        SAL_WARN("configmgr.dconf""too long int list for key " << key);
        return false;
    }
    css::uno::Sequence<sal_Int32> v(static_cast<sal_Int32>(n));
    static_assert(sizeof (sal_Int32) == sizeof (gint32), "size mismatch");
    std::memcpy(v.getArray(), p, n * sizeof (gint32));
        // assuming that n * sizeof (gint32) is small enough for std::size_t
    *value <<= v;
    return true;
}

bool getLongList(
    OString const & key, GVariantHolder const & variant, css::uno::Any * value)
{
    assert(value != nullptr);
    if (std::strcmp(g_variant_get_type_string(variant.get()), "ax") != 0) {
        SAL_WARN(
            "configmgr.dconf",
            "bad key " << key << " does not match long list property");
        return false;
    }
    gsize n;
    gconstpointer p = g_variant_get_fixed_array(
        variant.get(), &n, sizeof (gint64));
    if (n > o3tl::make_unsigned(
            std::numeric_limits<sal_Int32>::max()))
    {
        SAL_WARN("configmgr.dconf""too long long list for key " << key);
        return false;
    }
    css::uno::Sequence<sal_Int64> v(static_cast<sal_Int32>(n));
    static_assert(sizeof (sal_Int64) == sizeof (gint64), "size mismatch");
    std::memcpy(v.getArray(), p, n * sizeof (gint64));
        // assuming that n * sizeof (gint64) is small enough for std::size_t
    *value <<= v;
    return true;
}

bool getDoubleList(
    OString const & key, GVariantHolder const & variant, css::uno::Any * value)
{
    assert(value != nullptr);
    if (std::strcmp(g_variant_get_type_string(variant.get()), "ad") != 0) {
        SAL_WARN(
            "configmgr.dconf",
            "bad key " << key << " does not match double list property");
        return false;
    }
    gsize n;
    gconstpointer p = g_variant_get_fixed_array(
        variant.get(), &n, sizeof (gdouble));
    if (n > o3tl::make_unsigned(
            std::numeric_limits<sal_Int32>::max()))
    {
        SAL_WARN("configmgr.dconf""too long double list for key " << key);
        return false;
    }
    css::uno::Sequence<double> v(static_cast<sal_Int32>(n));
    static_assert(std::is_same<double, gdouble>::value, "type mismatch");
    std::memcpy(v.getArray(), p, n * sizeof (gdouble));
        // assuming that n * sizeof (gdouble) is small enough for std::size_t
    *value <<= v;
    return true;
}

bool getStringList(
    OString const & key, GVariantHolder const & variant, css::uno::Any * value)
{
    assert(value != nullptr);
    if (std::strcmp(g_variant_get_type_string(variant.get()), "as") != 0) {
        SAL_WARN(
            "configmgr.dconf",
            "bad key " << key << " does not match string list property");
        return false;
    }
    gsize n = g_variant_n_children(variant.get());
    if (n > o3tl::make_unsigned(
            std::numeric_limits<sal_Int32>::max()))
    {
        SAL_WARN("configmgr.dconf""too long string list for key " << key);
        return false;
    }
    css::uno::Sequence<OUString> v(static_cast<sal_Int32>(n));
    for (gsize i = 0; i != n; ++i) {
        GVariantHolder c(g_variant_get_child_value(variant.get(), i));
        if (!getStringValue(key, c, v.getArray() + i)) {
            return false;
        }
    }
    *value <<= v;
    return true;
}

bool getHexbinaryList(
    OString const & key, GVariantHolder const & variant, css::uno::Any * value)
{
    assert(value != nullptr);
    if (std::strcmp(g_variant_get_type_string(variant.get()), "aay") != 0) {
        SAL_WARN(
            "configmgr.dconf",
            "bad key " << key << " does not match hexbinary list property");
        return false;
    }
    gsize n = g_variant_n_children(variant.get());
    if (n > o3tl::make_unsigned(
            std::numeric_limits<sal_Int32>::max()))
    {
        SAL_WARN("configmgr.dconf""too long hexbinary list for key " << key);
        return false;
    }
    css::uno::Sequence<css::uno::Sequence<sal_Int8>> v(
        static_cast<sal_Int32>(n));
    for (gsize i = 0; i != n; ++i) {
        GVariantHolder c(g_variant_get_child_value(variant.get(), i));
        if (!getHexbinaryValue(key, c, v.getArray() + i)) {
            return false;
        }
    }
    *value <<= v;
    return true;
}

bool getAny(
    OString const & key, GVariantHolder const & variant, css::uno::Any * value)
{
    char const * t = g_variant_get_type_string(variant.get());
    if (std::strcmp(t, "b") == 0) {
        return getBoolean(key, variant, value);
    }
    if (std::strcmp(t, "n") == 0) {
        return getShort(key, variant, value);
    }
    if (std::strcmp(t, "i") == 0) {
        return getInt(key, variant, value);
    }
    if (std::strcmp(t, "x") == 0) {
        return getLong(key, variant, value);
    }
    if (std::strcmp(t, "d") == 0) {
        return getDouble(key, variant, value);
    }
    if (std::strcmp(t, "s") == 0) {
        return getString(key, variant, value);
    }
    if (std::strcmp(t, "ay") == 0) {
        return getHexbinary(key, variant, value);
    }
    if (std::strcmp(t, "ab") == 0) {
        return getBooleanList(key, variant, value);
    }
    if (std::strcmp(t, "an") == 0) {
        return getShortList(key, variant, value);
    }
    if (std::strcmp(t, "ai") == 0) {
        return getIntList(key, variant, value);
    }
    if (std::strcmp(t, "ax") == 0) {
        return getLongList(key, variant, value);
    }
    if (std::strcmp(t, "ad") == 0) {
        return getDoubleList(key, variant, value);
    }
    if (std::strcmp(t, "as") == 0) {
        return getStringList(key, variant, value);
    }
    if (std::strcmp(t, "aay") == 0) {
        return getHexbinaryList(key, variant, value);
    }
    SAL_WARN(
        "configmgr.dconf""bad key " << key << " does not match any property");
    return false;
}

enum class ReadValue { Error, Value, Remove };

ReadValue readValue(
    GObjectHolder<DConfClient> const & client, OString const & path, Type type,
    bool nillable, bool removable, css::uno::Any * value)
{
    assert(value != nullptr);
    assert(!value->hasValue());
    assert(!path.endsWith("/"));
    GVariantHolder v(dconf_client_read(client.get(), path.getStr()));
    if (v.get() == nullptr) {
        SAL_WARN("configmgr.dconf""cannot read key " << path);
        return ReadValue::Error;
    }
    if (removable && std::strcmp(g_variant_get_type_string(v.get()), "()") == 0)
    {
        return ReadValue::Remove;
    }
    bool nil;
    if (nillable) {
        if (g_variant_classify(v.get()) != G_VARIANT_CLASS_MAYBE) {
            SAL_WARN(
                "configmgr.dconf",
                "bad key " << path << " does not match nillable property");
        }
        v.reset(g_variant_get_maybe(v.get()));
        nil = v.get() == nullptr;
    } else {
        nil = false;
    }
    if (!nil) {
        switch (type) {
        case TYPE_ANY:
            if (!getAny(path, v, value)) {
                return ReadValue::Error;
            }
            break;
        case TYPE_BOOLEAN:
            if (!getBoolean(path, v, value)) {
                return ReadValue::Error;
            }
            break;
        case TYPE_SHORT:
            if (!getShort(path, v, value)) {
                return ReadValue::Error;
            }
            break;
        case TYPE_INT:
            if (!getInt(path, v, value)) {
                return ReadValue::Error;
            }
            break;
        case TYPE_LONG:
            if (!getLong(path, v, value)) {
                return ReadValue::Error;
            }
            break;
        case TYPE_DOUBLE:
            if (!getDouble(path, v, value)) {
                return ReadValue::Error;
            }
            break;
        case TYPE_STRING:
            if (!getString(path, v, value)) {
                return ReadValue::Error;
            }
            break;
        case TYPE_HEXBINARY:
            if (!getHexbinary(path, v, value)) {
                return ReadValue::Error;
            }
            break;
        case TYPE_BOOLEAN_LIST:
            if (!getBooleanList(path, v, value)) {
                return ReadValue::Error;
            }
            break;
        case TYPE_SHORT_LIST:
            if (!getShortList(path, v, value)) {
                return ReadValue::Error;
            }
            break;
        case TYPE_INT_LIST:
            if (!getIntList(path, v, value)) {
                return ReadValue::Error;
            }
            break;
        case TYPE_LONG_LIST:
            if (!getLongList(path, v, value)) {
                return ReadValue::Error;
            }
            break;
        case TYPE_DOUBLE_LIST:
            if (!getDoubleList(path, v, value)) {
                return ReadValue::Error;
            }
            break;
        case TYPE_STRING_LIST:
            if (!getStringList(path, v, value)) {
                return ReadValue::Error;
            }
            break;
        case TYPE_HEXBINARY_LIST:
            if (!getHexbinaryList(path, v, value)) {
                return ReadValue::Error;
            }
            break;
        default:
            assert(false); // cannot happen
        }
    }
    return ReadValue::Value;
}

void finalize(
    GObjectHolder<DConfClient> const & client, OString const & path,
    rtl::Reference<Node> const & node, int layer)
{
    if (!dconf_client_is_writable(client.get(), path.getStr())) {
        node->setFinalized(layer);
    }
}

void readDir(
    Data & data, int layer, rtl::Reference<Node> const & node,
    NodeMap & members, GObjectHolder<DConfClient> const & client,
    OString const & dir)
{
    StringArrayHolder a(dconf_client_list(client.get(), dir.getStr(), nullptr));
    for (char const * const * p = a.get(); *p != nullptr; ++p) {
        std::size_t n = std::strlen(*p);
        if (n > o3tl::make_unsigned(
                std::numeric_limits<sal_Int32>::max()))
        {
            SAL_WARN("configmgr.dconf""too long dir/key in dir " << dir);
            continue;
        }
        OString s(*p, static_cast<sal_Int32>(n));
        OString path(dir + s);
        OUString name;
        if (!rtl_convertStringToUString(
                &name.pData, s.getStr(), s.getLength(), RTL_TEXTENCODING_UTF8,
                (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
                 | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
                 | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
        {
            SAL_WARN("configmgr.dconf""non--UTF-8 dir/key in dir " << dir);
            continue;
        }
        bool isDir = name.endsWith("/", &name);
        OUString templ;
        bool remove;
        bool replace;
        if (node.is() && node->kind() == Node::KIND_SET) {
            if (!isDir) {
                SAL_WARN(
                    "configmgr.dconf",
                    "bad key " << path << " does not match set element");
                continue;
            }
            if (!decode(&name, true)) {
                continue;
            }
            enum class Op { None, Fuse, Replace, Remove };
            Op op = Op::None;
            bool content = false;
            bool bad = false;
            StringArrayHolder a2(
                dconf_client_list(client.get(), path.getStr(), nullptr));
            for (char const * const * p2 = a2.get(); *p2 != nullptr; ++p2) {
                if (std::strcmp(*p2, "op") == 0) {
                    OString path2(path + "op");
                    GVariantHolder v(
                        dconf_client_read(client.get(), path2.getStr()));
                    if (v.get() == nullptr) {
                        SAL_WARN(
                            "configmgr.dconf""cannot read key " << path2);
                        bad = true;
                        break;
                    }
                    OUString ops;
                    if (!getStringValue(path2, v, &ops)) {
                        bad = true;
                        break;
                    }
                    if (ops == "fuse") {
                        op = Op::Fuse;
                    } else if (ops == "replace") {
                        op = Op::Replace;
                    } else if (ops == "remove") {
                        op = Op::Remove;
                    } else {
                        SAL_WARN(
                            "configmgr.dconf",
                            "bad key " << path2 << " value " << ops);
                        bad = true;
                        break;
                    }
                } else if (std::strcmp(*p2, "template") == 0) {
                    OString path2(path + "template");
                    GVariantHolder v(
                        dconf_client_read(client.get(), path2.getStr()));
                    if (v.get() == nullptr) {
                        SAL_WARN(
                            "configmgr.dconf""cannot read key " << path2);
                        bad = true;
                        break;
                    }
                    if (!getStringValue(path2, v, &templ)) {
                        bad = true;
                        break;
                    }
                    if (!static_cast<SetNode *>(node.get())->
                        isValidTemplate(templ))
                    {
                        SAL_WARN(
                            "configmgr.dconf",
                            "bad key " << path2 << " value " << templ
                                << " denotes unsupported set element template");
                        bad = true;
                        break;
                    }
                } else if (std::strcmp(*p2, "content/") == 0) {
                    content = true;
                } else {
                    SAL_WARN(
                        "configmgr.dconf",
                        "bad dir/key " << p2
                            << " in set element indirection dir " << path);
                    bad = true;
                    break;
                }
            }
            if (bad) {
                continue;
            }
            switch (op) {
            default// case Op::None:
                SAL_WARN(
                    "configmgr.dconf",
                    "bad set element indirection dir " << path
                        << " missing \"op\" key");
                continue;
            case Op::Fuse:
            case Op::Replace:
                if (templ.isEmpty() || !content) {
                    SAL_WARN(
                        "configmgr.dconf",
                        "missing \"content\" and/or \"template\" dir/key in "
                            "\"op\" = \"fuse\"/\"remove\" set element"
                            " indirection dir " << path);
                    continue;
                }
                path += "content/";
                remove = false;
                replace = op == Op::Replace;
                break;
            case Op::Remove:
                if (!templ.isEmpty() || content) {
                    SAL_WARN(
                        "configmgr.dconf",
                        "bad \"content\" and/or \"template\" dir/key in \"op\" "
                            "= \"remove\" set element indirection dir "
                            << path);
                    continue;
                }
                remove = true;
                replace = false;
                break;
            }
        } else {
            remove = false;
            replace = false;
        }
        rtl::Reference<Node> member(members.findNode(layer, name));
        bool insert = !member.is();
        if (!remove) {
            if (replace || insert) {
                if (!node.is()) {
                    SAL_WARN("configmgr.dconf""bad unmatched " << path);
                    continue;
                }
                switch (node->kind()) {
                case Node::KIND_LOCALIZED_PROPERTY:
                    member.set(new LocalizedValueNode(layer));
                    break;
                case Node::KIND_GROUP:
                    if (!static_cast<GroupNode *>(node.get())->isExtensible()) {
                        SAL_WARN("configmgr.dconf""bad unmatched " << path);
                        continue;
                    }
                    member.set(
                        new PropertyNode(
                            layer, TYPE_ANY, true, css::uno::Any(), true));
                    break;
                case Node::KIND_SET:
                    assert(!templ.isEmpty());
                    member = data.getTemplate(layer, templ);
                    if (!member.is()) {
                        SAL_WARN(
                            "configmgr.dconf",
                            "bad " << path << " denoting undefined template "
                                << templ);
                        continue;
                    }
                    member = member->clone(true);
                    break;
                default:
                    assert(false); // cannot happen
                }
            } else if (!templ.isEmpty() && templ != member->getTemplateName()) {
                SAL_WARN(
                    "configmgr.dconf",
                    "bad " << path
                        << " denoting set element of non-matching template "
                        << member->getTemplateName());
                continue;
            }
        }
        if (member.is()) {
            if (member->getFinalized() < layer) {
                continue;
            }
            switch (member->kind()) {
            case Node::KIND_PROPERTY:
                {
                    if (isDir) {
                        SAL_WARN(
                            "configmgr.dconf",
                            "bad dir " << path << " does not match property");
                        continue;
                    }
                    rtl::Reference<PropertyNode> prop(
                        static_cast<PropertyNode *>(member.get()));
                    css::uno::Any value;
                    switch (readValue(
                                client, path, prop->getStaticType(),
                                prop->isNillable(), prop->isExtension(),
                                &value))
                    {
                    case ReadValue::Error:
                        continue;
                    case ReadValue::Value:
                        prop->setValue(layer, value, false);
                        finalize(client, path, member, layer);
                        break;
                    case ReadValue::Remove:
                        remove = true;
                        break;
                    }
                    break;
                }
            case Node::KIND_LOCALIZED_VALUE:
                {
                    if (isDir) {
                        SAL_WARN(
                            "configmgr.dconf",
                            "bad dir " << path
                                << " does not match localized value");
                        continue;
                    }
                    assert(
                        node.is()
                        && node->kind() == Node::KIND_LOCALIZED_PROPERTY);
                    rtl::Reference<LocalizedPropertyNode> locProp(
                        static_cast<LocalizedPropertyNode *>(node.get()));
                    css::uno::Any value;
                    if (readValue(
                            client, path, locProp->getStaticType(),
                            locProp->isNillable(), false, &value)
                        == ReadValue::Error)
                    {
                        continue;
                    }
                    static_cast<LocalizedValueNode *>(member.get())->setValue(
                        layer, value, false);
                    finalize(client, path, member, layer);
                    break;
                }
            case Node::KIND_LOCALIZED_PROPERTY:
            case Node::KIND_GROUP:
            case Node::KIND_SET:
                if (!isDir) {
                    SAL_WARN(
                        "configmgr.dconf",
                        "bad key " << path
                            << " does not match localized property, group, or"
                            " set, respectively");
                    continue;
                }
                assert(path.endsWith("/"));
                readDir(
                    data, layer, member, member->getMembers(), client, path);
                break;
            default:
                assert(false); // cannot happen
            }
        }
        if (remove) {
            if (!(member.is() && member->getMandatory())) {
                members.erase(name);
            }
        } else if (replace) {
            members.erase(name);
            members.insert(NodeMap::value_type(name, member));
        } else if (insert) {
            members.insert(NodeMap::value_type(name, member));
        }
    }
}

OString encodeSegment(OUString const & name, bool setElement) {
    if (!setElement) {
        return name.toUtf8();
    }
    OUStringBuffer buf;
    for (sal_Int32 i = 0; i != name.getLength(); ++i) {
        sal_Unicode c = name[i];
        switch (c) {
        case '\0':
            buf.append("\\00");
            break;
        case '/':
            buf.append("\\2F");
            break;
        case '\\':
            buf.append("\\5C");
            break;
        default:
            buf.append(c);
        }
    }
    return buf.makeStringAndClear().toUtf8();
}

OString encodeString(OUString const & value) {
    OUStringBuffer buf;
    for (sal_Int32 i = 0; i != value.getLength(); ++i) {
        sal_Unicode c = value[i];
        switch (c) {
        case '\0':
            buf.append("\\00");
            break;
        case '\\':
            buf.append("\\5C");
            break;
        default:
            buf.append(c);
        }
    }
    return buf.makeStringAndClear().toUtf8();
}

bool addProperty(
    ChangesetHolder const & changeset, OString const & pathRepresentation,
    Type type, bool nillable, css::uno::Any const & value)
{
    Type dynType = getDynamicType(value);
    assert(dynType != TYPE_ERROR);
    if (type == TYPE_ANY) {
        type = dynType;
    }
    GVariantHolder v;
    std::forward_list<GVariantHolder> children;
    if (dynType == TYPE_NIL) {
        switch (type) {
        case TYPE_BOOLEAN:
            v.reset(g_variant_new_maybe(G_VARIANT_TYPE_BOOLEAN, nullptr));
            break;
        case TYPE_SHORT:
            v.reset(g_variant_new_maybe(G_VARIANT_TYPE_INT16, nullptr));
            break;
        case TYPE_INT:
            v.reset(g_variant_new_maybe(G_VARIANT_TYPE_INT32, nullptr));
            break;
        case TYPE_LONG:
            v.reset(g_variant_new_maybe(G_VARIANT_TYPE_INT64, nullptr));
            break;
        case TYPE_DOUBLE:
            v.reset(g_variant_new_maybe(G_VARIANT_TYPE_DOUBLE, nullptr));
            break;
        case TYPE_STRING:
            v.reset(g_variant_new_maybe(G_VARIANT_TYPE_STRING, nullptr));
            break;
        case TYPE_HEXBINARY:
        case TYPE_BOOLEAN_LIST:
        case TYPE_SHORT_LIST:
        case TYPE_INT_LIST:
        case TYPE_LONG_LIST:
        case TYPE_DOUBLE_LIST:
        case TYPE_STRING_LIST:
        case TYPE_HEXBINARY_LIST:
            {
                static char const * const typeString[
                    TYPE_HEXBINARY_LIST - TYPE_HEXBINARY + 1]
                    = { "ay""ab""an""ai""ax""ad""as""aay" };
                GVariantTypeHolder ty(
                    g_variant_type_new(typeString[type - TYPE_HEXBINARY]));
                if (ty.get() == nullptr) {
                    SAL_WARN("configmgr.dconf""g_variant_type_new failed");
                    return false;
                }
                v.reset(g_variant_new_maybe(ty.get(), nullptr));
                break;
            }
        default:
            assert(false); // this cannot happen
            break;
        }
        if (v.get() == nullptr) {
            SAL_WARN("configmgr.dconf""g_variant_new_maybe failed");
            return false;
        }
    } else {
        switch (type) {
        case TYPE_BOOLEAN:
            v.reset(g_variant_new_boolean(value.get<bool>()));
            break;
        case TYPE_SHORT:
            v.reset(g_variant_new_int16(value.get<sal_Int16>()));
            break;
        case TYPE_INT:
            v.reset(g_variant_new_int32(value.get<sal_Int32>()));
            break;
        case TYPE_LONG:
            v.reset(g_variant_new_int64(value.get<sal_Int64>()));
            break;
        case TYPE_DOUBLE:
            v.reset(g_variant_new_double(value.get<double>()));
            break;
        case TYPE_STRING:
            v.reset(
                g_variant_new_string(
                    encodeString(value.get<OUString>()).getStr()));
            break;
        case TYPE_HEXBINARY:
            {
                css::uno::Sequence<sal_Int8> seq(
                    value.get<css::uno::Sequence<sal_Int8>>());
                static_assert(
                    sizeof(sal_Int32) <= sizeof(gsize),
                    "G_MAXSIZE too small");
                static_assert(
                    sizeof (sal_Int8) == sizeof (guchar), "size mismatch");
                v.reset(
                    g_variant_new_fixed_array(
                        G_VARIANT_TYPE_BYTE, seq.getConstArray(),
                        seq.getLength(), sizeof (sal_Int8)));
                break;
            }
        case TYPE_BOOLEAN_LIST:
            {
                css::uno::Sequence<sal_Bool> seq(
                    value.get<css::uno::Sequence<sal_Bool>>());
                static_assert(
                    sizeof(sal_Int32) <= sizeof(gsize),
                    "G_MAXSIZE too small");
                static_assert(sizeof (sal_Bool) == 1, "size mismatch");
                v.reset(
                    g_variant_new_fixed_array(
                        G_VARIANT_TYPE_BOOLEAN, seq.getConstArray(),
                        seq.getLength(), sizeof (sal_Bool)));
                break;
            }
        case TYPE_SHORT_LIST:
            {
                css::uno::Sequence<sal_Int16> seq(
                    value.get<css::uno::Sequence<sal_Int16>>());
                static_assert(
                    sizeof(sal_Int32) <= sizeof(gsize),
                    "G_MAXSIZE too small");
                static_assert(
                    sizeof (sal_Int16) == sizeof (gint16), "size mismatch");
                v.reset(
                    g_variant_new_fixed_array(
                        G_VARIANT_TYPE_INT16, seq.getConstArray(),
                        seq.getLength(), sizeof (sal_Int16)));
                    //TODO: endian-ness?
                break;
            }
        case TYPE_INT_LIST:
            {
                css::uno::Sequence<sal_Int32> seq(
                    value.get<css::uno::Sequence<sal_Int32>>());
                static_assert(
                    sizeof(sal_Int32) <= sizeof(gsize),
                    "G_MAXSIZE too small");
                static_assert(
                    sizeof (sal_Int32) == sizeof (gint32), "size mismatch");
                v.reset(
                    g_variant_new_fixed_array(
                        G_VARIANT_TYPE_INT32, seq.getConstArray(),
                        seq.getLength(), sizeof (sal_Int32)));
                    //TODO: endian-ness?
                break;
            }
        case TYPE_LONG_LIST:
            {
                css::uno::Sequence<sal_Int64> seq(
                    value.get<css::uno::Sequence<sal_Int64>>());
                static_assert(
                    sizeof(sal_Int32) <= sizeof(gsize),
                    "G_MAXSIZE too small");
                static_assert(
                    sizeof (sal_Int64) == sizeof (gint64), "size mismatch");
                v.reset(
                    g_variant_new_fixed_array(
                        G_VARIANT_TYPE_INT64, seq.getConstArray(),
                        seq.getLength(), sizeof (sal_Int64)));
                    //TODO: endian-ness?
                break;
            }
        case TYPE_DOUBLE_LIST:
            {
                css::uno::Sequence<double> seq(
                    value.get<css::uno::Sequence<double>>());
                static_assert(
                    sizeof(sal_Int32) <= sizeof(gsize),
                    "G_MAXSIZE too small");
                static_assert(
                    sizeof (double) == sizeof (gdouble), "size mismatch");
                v.reset(
                    g_variant_new_fixed_array(
                        G_VARIANT_TYPE_DOUBLE, seq.getConstArray(),
                        seq.getLength(), sizeof (double)));
                    //TODO: endian-ness?
                break;
            }
        case TYPE_STRING_LIST:
            {
                const css::uno::Sequence<OUString> seq(
                    value.get<css::uno::Sequence<OUString>>());
                std::vector<GVariant *> vs;
                for (OUString const & s : seq) {
                    children.emplace_front(
                        g_variant_new_string(encodeString(s).getStr()));
                    if (children.front().get() == nullptr) {
                        SAL_WARN(
                            "configmgr.dconf""g_variant_new_string failed");
                        return false;
                    }
                    vs.push_back(children.front().get());
                }
                static_assert(
                    sizeof(sal_Int32) <= sizeof(gsize),
                    "G_MAXSIZE too small");
                v.reset(
                    g_variant_new_array(
                        G_VARIANT_TYPE_STRING, vs.data(), seq.getLength()));
                break;
            }
        case TYPE_HEXBINARY_LIST:
            {
                const css::uno::Sequence<css::uno::Sequence<sal_Int8>> seqSeq(
                    value.get<
                        css::uno::Sequence<css::uno::Sequence<sal_Int8>>>());
                std::vector<GVariant *> vs;
                for (css::uno::Sequence<sal_Int8> const & seq : seqSeq) {
                    static_assert(
                        sizeof(sal_Int32) <= sizeof(gsize),
                        "G_MAXSIZE too small");
                    static_assert(
                        sizeof (sal_Int8) == sizeof (guchar), "size mismatch");
                    children.emplace_front(
                        g_variant_new_fixed_array(
                            G_VARIANT_TYPE_BYTE, seq.getConstArray(),
                            seq.getLength(), sizeof (sal_Int8)));
                    if (children.front().get() == nullptr) {
                        SAL_WARN(
                            "configmgr.dconf",
                            "g_variant_new_fixed_array failed");
                        return false;
                    }
                    vs.push_back(children.front().get());
                }
                GVariantTypeHolder ty(g_variant_type_new("aay"));
                if (ty.get() == nullptr) {
                    SAL_WARN("configmgr.dconf""g_variant_type_new failed");
                    return false;
                }
                static_assert(
                    sizeof(sal_Int32) <= sizeof(gsize),
                    "G_MAXSIZE too small");
                v.reset(
                    g_variant_new_array(ty.get(), vs.data(), seqSeq.getLength()));
                break;
            }
        default:
            assert(false); // this cannot happen
            break;
        }
        if (v.get() == nullptr) {
            SAL_WARN("configmgr.dconf""GVariant creation failed");
            return false;
        }
        if (nillable) {
            GVariantHolder v1(g_variant_new_maybe(nullptr, v.get()));
            if (v1.get() == nullptr) {
                SAL_WARN("configmgr.dconf""g_variant_new_maybe failed");
                return false;
            }
            v.release();
            v.reset(v1.get());
            v1.release();
        }
    }
    dconf_changeset_set(
        changeset.get(), pathRepresentation.getStr(), v.get());
    for (auto & i: children) {
        i.release();
    }
    v.release();
    return true;
}

bool addNode(
    Components & components, ChangesetHolder const & changeset,
    rtl::Reference<Node> const & parent, OString const & pathRepresentation,
    rtl::Reference<Node> const & node)
{
    switch (node->kind()) {
    case Node::KIND_PROPERTY:
        {
            PropertyNode * prop = static_cast<PropertyNode *>(node.get());
            if (!addProperty(
                    changeset, pathRepresentation, prop->getStaticType(),
                    prop->isNillable(), prop->getValue(components)))
            {
                return false;
            }
            break;
        }
    case Node::KIND_LOCALIZED_VALUE:
        {
            //TODO: name.isEmpty()?
            LocalizedPropertyNode * locprop
                = static_cast<LocalizedPropertyNode *>(parent.get());
            if (!addProperty(
                    changeset, pathRepresentation,
                    locprop->getStaticType(), locprop->isNillable(),
                    static_cast<LocalizedValueNode *>(node.get())->getValue()))
            {
                return false;
            }
            break;
        }
    case Node::KIND_LOCALIZED_PROPERTY:
    case Node::KIND_GROUP:
    case Node::KIND_SET:
        for (auto const & i: node->getMembers()) {
            OUString templ(i.second->getTemplateName());
            OString path(
                pathRepresentation + "/"
                + encodeSegment(i.first, !templ.isEmpty()));
            if (!templ.isEmpty()) {
                path += "/";
                GVariantHolder v(g_variant_new_string("replace"));
                if (v.get() == nullptr) {
                    SAL_WARN("configmgr.dconf""g_variant_new_string failed");
                    return false;
                }
                dconf_changeset_set(
                    changeset.get(), OString(path + "op").getStr(), v.get());
                v.release();
                v.reset(g_variant_new_string(encodeString(templ).getStr()));
                if (v.get() == nullptr) {
                    SAL_WARN("configmgr.dconf""g_variant_new_string failed");
                    return false;
                }
                dconf_changeset_set(
                    changeset.get(), OString(path + "template").getStr(),
                    v.get());
                v.release();
                path += "content";
            }
            if (!addNode(components, changeset, parent, path, i.second)) {
                return false;
            }
        }
        break;
    case Node::KIND_ROOT:
        assert(false); // this cannot happen
        break;
    }
    return true;
}

bool addModifications(
    Components & components, ChangesetHolder const & changeset,
    OString const & parentPathRepresentation,
    rtl::Reference<Node> const & parent, OUString const & nodeName,
    rtl::Reference<Node> const & node,
    Modifications::Node const & modifications)
{
    // It is never necessary to write oor:finalized or oor:mandatory attributes,
    // as they cannot be set via the UNO API.
    if (modifications.children.empty()) {
        assert(parent.is());
            // components themselves have no parent but must have children
        if (node.is()) {
            OUString templ(node->getTemplateName());
            OString path(
                parentPathRepresentation + "/"
                + encodeSegment(nodeName, !templ.isEmpty()));
            if (!templ.isEmpty()) {
                path += "/";
                GVariantHolder v(g_variant_new_string("replace"));
                if (v.get() == nullptr) {
                    SAL_WARN("configmgr.dconf""g_variant_new_string failed");
                    return false;
                }
                dconf_changeset_set(
                    changeset.get(), OString(path + "op").getStr(), v.get());
                v.release();
                v.reset(g_variant_new_string(encodeString(templ).getStr()));
                if (v.get() == nullptr) {
                    SAL_WARN("configmgr.dconf""g_variant_new_string failed");
                    return false;
                }
                dconf_changeset_set(
                    changeset.get(), OString(path + "template").getStr(),
                    v.get());
                v.release();
                path += "content";
            }
            if (!addNode(components, changeset, parent, path, node)) {
                return false;
            }
        } else {
            switch (parent->kind()) {
            case Node::KIND_LOCALIZED_PROPERTY:
            case Node::KIND_GROUP:
                {
                    GVariantHolder v(g_variant_new_tuple(nullptr, 0));
                    if (v.get() == nullptr) {
                        SAL_WARN(
                            "configmgr.dconf""g_variant_new_tuple failed");
                        return false;
                    }
                    OString path(parentPathRepresentation);
                    if (!nodeName.isEmpty()) { // KIND_LOCALIZED_PROPERTY
                        path += "/" + encodeSegment(nodeName, false);
                    }
                    dconf_changeset_set(
                        changeset.get(), path.getStr(), v.get());
                    v.release();
                    break;
                }
            case Node::KIND_SET:
                {
                    OString path(
                        parentPathRepresentation + "/"
                        + encodeSegment(nodeName, true) + "/");
                    GVariantHolder v(g_variant_new_string("remove"));
                    if (v.get() == nullptr) {
                        SAL_WARN(
                            "configmgr.dconf""g_variant_new_string failed");
                        return false;
                    }
                    dconf_changeset_set(
                        changeset.get(), OString(path + "op").getStr(),
                        v.get());
                    v.release();
                    dconf_changeset_set(
                        changeset.get(), OString(path + "template").getStr(),
                        nullptr);
                    dconf_changeset_set(
                        changeset.get(), OString(path + "content/").getStr(),
                        nullptr);
                    break;
                }
            default:
                assert(false); // this cannot happen
                break;
            }
        }
    } else {
        assert(node.is());
        OUString templ(node->getTemplateName());
        OString path(
            parentPathRepresentation + "/"
            + encodeSegment(nodeName, !templ.isEmpty()));
        if (!templ.isEmpty()) {
            path += "/";
            GVariantHolder v(g_variant_new_string("fuse"));
            if (v.get() == nullptr) {
                SAL_WARN("configmgr.dconf""g_variant_new_string failed");
                return false;
            }
            dconf_changeset_set(
                changeset.get(), OString(path + "op").getStr(), v.get());
            v.release();
            v.reset(g_variant_new_string(encodeString(templ).getStr()));
            if (v.get() == nullptr) {
                SAL_WARN("configmgr.dconf""g_variant_new_string failed");
                return false;
            }
            dconf_changeset_set(
                changeset.get(), OString(path + "template").getStr(), v.get());
            v.release();
            path += "content";
        }
        for (auto const & i: modifications.children) {
            if (!addModifications(
                    components, changeset, path, node, i.first,
                    node->getMember(i.first), i.second))
            {
                return false;
            }
        }
    }
    return true;
}

}

void readLayer(Data & data, int layer) {
    GObjectHolder<DConfClient> client(dconf_client_new());
    if (client.get() == nullptr) {
        SAL_WARN("configmgr.dconf""dconf_client_new failed");
        return;
    }
    readDir(
        data, layer, rtl::Reference<Node>(), data.getComponents(), client,
        getRoot() + "/");
}

void writeModifications(Components & components, Data & data) {
    GObjectHolder<DConfClient> client(dconf_client_new());
    if (client.get() == nullptr) {
        SAL_WARN("configmgr.dconf""dconf_client_new failed");
    }
    ChangesetHolder cs(dconf_changeset_new());
    if (cs.get() == nullptr) {
        SAL_WARN("configmgr.dconf""dconf_changeset_new failed");
        return;
    }
    for (auto const & i: data.modifications.getRoot().children) {
        if (!addModifications(
                components, cs, getRoot(), rtl::Reference<Node>(), i.first,
                data.getComponents().findNode(Data::NO_LAYER, i.first),
                i.second))
        {
            return;
        }
    }
    if (!dconf_client_change_sync(
            client.get(), cs.get(), nullptr, nullptr, nullptr))
    {
        //TODO: GError
        SAL_WARN("configmgr.dconf""dconf_client_change_sync failed");
        return;
    }
    data.modifications.clear();
}

}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5
C=94 H=97 G=95

¤ Dauer der Verarbeitung: 0.8 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.