|  | /* GDBus - GLib D-Bus Library | 
|  | * | 
|  | * Copyright (C) 2008-2010 Red Hat, Inc. | 
|  | * | 
|  | * SPDX-License-Identifier: LGPL-2.1-or-later | 
|  | * | 
|  | * This library is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU Lesser General Public | 
|  | * License as published by the Free Software Foundation; either | 
|  | * version 2.1 of the License, or (at your option) any later version. | 
|  | * | 
|  | * This library is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | * Lesser General Public License for more details. | 
|  | * | 
|  | * You should have received a copy of the GNU Lesser General | 
|  | * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. | 
|  | * | 
|  | * Author: David Zeuthen <davidz@redhat.com> | 
|  | */ | 
|  |  | 
|  | #include "config.h" | 
|  |  | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  |  | 
|  | #include "gdbusintrospection.h" | 
|  |  | 
|  | #include "glibintl.h" | 
|  |  | 
|  | /* ---------------------------------------------------------------------------------------------------- */ | 
|  |  | 
|  | #define _MY_DEFINE_BOXED_TYPE(TypeName, type_name) \ | 
|  | G_DEFINE_BOXED_TYPE (TypeName, type_name, type_name##_ref, type_name##_unref) | 
|  |  | 
|  | _MY_DEFINE_BOXED_TYPE (GDBusNodeInfo,       g_dbus_node_info) | 
|  | _MY_DEFINE_BOXED_TYPE (GDBusInterfaceInfo,  g_dbus_interface_info) | 
|  | _MY_DEFINE_BOXED_TYPE (GDBusMethodInfo,     g_dbus_method_info) | 
|  | _MY_DEFINE_BOXED_TYPE (GDBusSignalInfo,     g_dbus_signal_info) | 
|  | _MY_DEFINE_BOXED_TYPE (GDBusPropertyInfo,   g_dbus_property_info) | 
|  | _MY_DEFINE_BOXED_TYPE (GDBusArgInfo,        g_dbus_arg_info) | 
|  | _MY_DEFINE_BOXED_TYPE (GDBusAnnotationInfo, g_dbus_annotation_info) | 
|  |  | 
|  | #undef _MY_DEFINE_BOXED_TYPE | 
|  |  | 
|  | /* ---------------------------------------------------------------------------------------------------- */ | 
|  |  | 
|  | typedef struct | 
|  | { | 
|  | /* stuff we are currently collecting */ | 
|  | GPtrArray *args; | 
|  | GPtrArray *out_args; | 
|  | GPtrArray *methods; | 
|  | GPtrArray *signals; | 
|  | GPtrArray *properties; | 
|  | GPtrArray *interfaces; | 
|  | GPtrArray *nodes; | 
|  | GPtrArray *annotations; | 
|  |  | 
|  | /* A list of GPtrArray's containing annotations */ | 
|  | GSList *annotations_stack; | 
|  |  | 
|  | /* A list of GPtrArray's containing interfaces */ | 
|  | GSList *interfaces_stack; | 
|  |  | 
|  | /* A list of GPtrArray's containing nodes */ | 
|  | GSList *nodes_stack; | 
|  |  | 
|  | /* Whether the direction was "in" for last parsed arg */ | 
|  | gboolean last_arg_was_in; | 
|  |  | 
|  | /* Number of args currently being collected; used for assigning | 
|  | * names to args without a "name" attribute | 
|  | */ | 
|  | guint num_args; | 
|  |  | 
|  | } ParseData; | 
|  |  | 
|  | /* ---------------------------------------------------------------------------------------------------- */ | 
|  |  | 
|  | /** | 
|  | * g_dbus_node_info_ref: | 
|  | * @info: A #GDBusNodeInfo | 
|  | * | 
|  | * If @info is statically allocated does nothing. Otherwise increases | 
|  | * the reference count. | 
|  | * | 
|  | * Returns: (not nullable): The same @info. | 
|  | * | 
|  | * Since: 2.26 | 
|  | */ | 
|  | GDBusNodeInfo * | 
|  | g_dbus_node_info_ref (GDBusNodeInfo *info) | 
|  | { | 
|  | if (g_atomic_int_get (&info->ref_count) == -1) | 
|  | return info; | 
|  | g_atomic_int_inc (&info->ref_count); | 
|  | return info; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * g_dbus_interface_info_ref: | 
|  | * @info: A #GDBusInterfaceInfo | 
|  | * | 
|  | * If @info is statically allocated does nothing. Otherwise increases | 
|  | * the reference count. | 
|  | * | 
|  | * Returns: (not nullable): The same @info. | 
|  | * | 
|  | * Since: 2.26 | 
|  | */ | 
|  | GDBusInterfaceInfo * | 
|  | g_dbus_interface_info_ref (GDBusInterfaceInfo *info) | 
|  | { | 
|  | if (g_atomic_int_get (&info->ref_count) == -1) | 
|  | return info; | 
|  | g_atomic_int_inc (&info->ref_count); | 
|  | return info; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * g_dbus_method_info_ref: | 
|  | * @info: A #GDBusMethodInfo | 
|  | * | 
|  | * If @info is statically allocated does nothing. Otherwise increases | 
|  | * the reference count. | 
|  | * | 
|  | * Returns: (not nullable): The same @info. | 
|  | * | 
|  | * Since: 2.26 | 
|  | */ | 
|  | GDBusMethodInfo * | 
|  | g_dbus_method_info_ref (GDBusMethodInfo *info) | 
|  | { | 
|  | if (g_atomic_int_get (&info->ref_count) == -1) | 
|  | return info; | 
|  | g_atomic_int_inc (&info->ref_count); | 
|  | return info; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * g_dbus_signal_info_ref: | 
|  | * @info: A #GDBusSignalInfo | 
|  | * | 
|  | * If @info is statically allocated does nothing. Otherwise increases | 
|  | * the reference count. | 
|  | * | 
|  | * Returns: (not nullable): The same @info. | 
|  | * | 
|  | * Since: 2.26 | 
|  | */ | 
|  | GDBusSignalInfo * | 
|  | g_dbus_signal_info_ref (GDBusSignalInfo *info) | 
|  | { | 
|  | if (g_atomic_int_get (&info->ref_count) == -1) | 
|  | return info; | 
|  | g_atomic_int_inc (&info->ref_count); | 
|  | return info; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * g_dbus_property_info_ref: | 
|  | * @info: A #GDBusPropertyInfo | 
|  | * | 
|  | * If @info is statically allocated does nothing. Otherwise increases | 
|  | * the reference count. | 
|  | * | 
|  | * Returns: (not nullable): The same @info. | 
|  | * | 
|  | * Since: 2.26 | 
|  | */ | 
|  | GDBusPropertyInfo * | 
|  | g_dbus_property_info_ref (GDBusPropertyInfo *info) | 
|  | { | 
|  | if (g_atomic_int_get (&info->ref_count) == -1) | 
|  | return info; | 
|  | g_atomic_int_inc (&info->ref_count); | 
|  | return info; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * g_dbus_arg_info_ref: | 
|  | * @info: A #GDBusArgInfo | 
|  | * | 
|  | * If @info is statically allocated does nothing. Otherwise increases | 
|  | * the reference count. | 
|  | * | 
|  | * Returns: (not nullable): The same @info. | 
|  | * | 
|  | * Since: 2.26 | 
|  | */ | 
|  | GDBusArgInfo * | 
|  | g_dbus_arg_info_ref (GDBusArgInfo *info) | 
|  | { | 
|  | if (g_atomic_int_get (&info->ref_count) == -1) | 
|  | return info; | 
|  | g_atomic_int_inc (&info->ref_count); | 
|  | return info; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * g_dbus_annotation_info_ref: | 
|  | * @info: A #GDBusNodeInfo | 
|  | * | 
|  | * If @info is statically allocated does nothing. Otherwise increases | 
|  | * the reference count. | 
|  | * | 
|  | * Returns: (not nullable): The same @info. | 
|  | * | 
|  | * Since: 2.26 | 
|  | */ | 
|  | GDBusAnnotationInfo * | 
|  | g_dbus_annotation_info_ref (GDBusAnnotationInfo *info) | 
|  | { | 
|  | if (g_atomic_int_get (&info->ref_count) == -1) | 
|  | return info; | 
|  | g_atomic_int_inc (&info->ref_count); | 
|  | return info; | 
|  | } | 
|  |  | 
|  | /* ---------------------------------------------------------------------------------------------------- */ | 
|  |  | 
|  | static void | 
|  | free_null_terminated_array (gpointer array, GDestroyNotify unref_func) | 
|  | { | 
|  | guint n; | 
|  | gpointer *p = array; | 
|  | if (p == NULL) | 
|  | return; | 
|  | for (n = 0; p[n] != NULL; n++) | 
|  | unref_func (p[n]); | 
|  | g_free (p); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * g_dbus_annotation_info_unref: | 
|  | * @info: A #GDBusAnnotationInfo. | 
|  | * | 
|  | * If @info is statically allocated, does nothing. Otherwise decreases | 
|  | * the reference count of @info. When its reference count drops to 0, | 
|  | * the memory used is freed. | 
|  | * | 
|  | * Since: 2.26 | 
|  | */ | 
|  | void | 
|  | g_dbus_annotation_info_unref (GDBusAnnotationInfo *info) | 
|  | { | 
|  | if (g_atomic_int_get (&info->ref_count) == -1) | 
|  | return; | 
|  | if (g_atomic_int_dec_and_test (&info->ref_count)) | 
|  | { | 
|  | g_free (info->key); | 
|  | g_free (info->value); | 
|  | free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref); | 
|  | g_free (info); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * g_dbus_arg_info_unref: | 
|  | * @info: A #GDBusArgInfo. | 
|  | * | 
|  | * If @info is statically allocated, does nothing. Otherwise decreases | 
|  | * the reference count of @info. When its reference count drops to 0, | 
|  | * the memory used is freed. | 
|  | * | 
|  | * Since: 2.26 | 
|  | */ | 
|  | void | 
|  | g_dbus_arg_info_unref (GDBusArgInfo *info) | 
|  | { | 
|  | if (g_atomic_int_get (&info->ref_count) == -1) | 
|  | return; | 
|  | if (g_atomic_int_dec_and_test (&info->ref_count)) | 
|  | { | 
|  | g_free (info->name); | 
|  | g_free (info->signature); | 
|  | free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref); | 
|  | g_free (info); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * g_dbus_method_info_unref: | 
|  | * @info: A #GDBusMethodInfo. | 
|  | * | 
|  | * If @info is statically allocated, does nothing. Otherwise decreases | 
|  | * the reference count of @info. When its reference count drops to 0, | 
|  | * the memory used is freed. | 
|  | * | 
|  | * Since: 2.26 | 
|  | */ | 
|  | void | 
|  | g_dbus_method_info_unref (GDBusMethodInfo *info) | 
|  | { | 
|  | if (g_atomic_int_get (&info->ref_count) == -1) | 
|  | return; | 
|  | if (g_atomic_int_dec_and_test (&info->ref_count)) | 
|  | { | 
|  | g_free (info->name); | 
|  | free_null_terminated_array (info->in_args, (GDestroyNotify) g_dbus_arg_info_unref); | 
|  | free_null_terminated_array (info->out_args, (GDestroyNotify) g_dbus_arg_info_unref); | 
|  | free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref); | 
|  | g_free (info); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * g_dbus_signal_info_unref: | 
|  | * @info: A #GDBusSignalInfo. | 
|  | * | 
|  | * If @info is statically allocated, does nothing. Otherwise decreases | 
|  | * the reference count of @info. When its reference count drops to 0, | 
|  | * the memory used is freed. | 
|  | * | 
|  | * Since: 2.26 | 
|  | */ | 
|  | void | 
|  | g_dbus_signal_info_unref (GDBusSignalInfo *info) | 
|  | { | 
|  | if (g_atomic_int_get (&info->ref_count) == -1) | 
|  | return; | 
|  | if (g_atomic_int_dec_and_test (&info->ref_count)) | 
|  | { | 
|  | g_free (info->name); | 
|  | free_null_terminated_array (info->args, (GDestroyNotify) g_dbus_arg_info_unref); | 
|  | free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref); | 
|  | g_free (info); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * g_dbus_property_info_unref: | 
|  | * @info: A #GDBusPropertyInfo. | 
|  | * | 
|  | * If @info is statically allocated, does nothing. Otherwise decreases | 
|  | * the reference count of @info. When its reference count drops to 0, | 
|  | * the memory used is freed. | 
|  | * | 
|  | * Since: 2.26 | 
|  | */ | 
|  | void | 
|  | g_dbus_property_info_unref (GDBusPropertyInfo *info) | 
|  | { | 
|  | if (g_atomic_int_get (&info->ref_count) == -1) | 
|  | return; | 
|  | if (g_atomic_int_dec_and_test (&info->ref_count)) | 
|  | { | 
|  | g_free (info->name); | 
|  | g_free (info->signature); | 
|  | free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref); | 
|  | g_free (info); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * g_dbus_interface_info_unref: | 
|  | * @info: A #GDBusInterfaceInfo. | 
|  | * | 
|  | * If @info is statically allocated, does nothing. Otherwise decreases | 
|  | * the reference count of @info. When its reference count drops to 0, | 
|  | * the memory used is freed. | 
|  | * | 
|  | * Since: 2.26 | 
|  | */ | 
|  | void | 
|  | g_dbus_interface_info_unref (GDBusInterfaceInfo *info) | 
|  | { | 
|  | if (g_atomic_int_get (&info->ref_count) == -1) | 
|  | return; | 
|  | if (g_atomic_int_dec_and_test (&info->ref_count)) | 
|  | { | 
|  | g_free (info->name); | 
|  | free_null_terminated_array (info->methods, (GDestroyNotify) g_dbus_method_info_unref); | 
|  | free_null_terminated_array (info->signals, (GDestroyNotify) g_dbus_signal_info_unref); | 
|  | free_null_terminated_array (info->properties, (GDestroyNotify) g_dbus_property_info_unref); | 
|  | free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref); | 
|  | g_free (info); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * g_dbus_node_info_unref: | 
|  | * @info: A #GDBusNodeInfo. | 
|  | * | 
|  | * If @info is statically allocated, does nothing. Otherwise decreases | 
|  | * the reference count of @info. When its reference count drops to 0, | 
|  | * the memory used is freed. | 
|  | * | 
|  | * Since: 2.26 | 
|  | */ | 
|  | void | 
|  | g_dbus_node_info_unref (GDBusNodeInfo *info) | 
|  | { | 
|  | if (g_atomic_int_get (&info->ref_count) == -1) | 
|  | return; | 
|  | if (g_atomic_int_dec_and_test (&info->ref_count)) | 
|  | { | 
|  | g_free (info->path); | 
|  | free_null_terminated_array (info->interfaces, (GDestroyNotify) g_dbus_interface_info_unref); | 
|  | free_null_terminated_array (info->nodes, (GDestroyNotify) g_dbus_node_info_unref); | 
|  | free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref); | 
|  | g_free (info); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* ---------------------------------------------------------------------------------------------------- */ | 
|  |  | 
|  | static void | 
|  | g_dbus_annotation_info_set (ParseData            *data, | 
|  | GDBusAnnotationInfo  *info, | 
|  | const gchar          *key, | 
|  | const gchar          *value, | 
|  | GDBusAnnotationInfo **embedded_annotations) | 
|  | { | 
|  | info->ref_count = 1; | 
|  |  | 
|  | if (key != NULL) | 
|  | info->key = g_strdup (key); | 
|  |  | 
|  | if (value != NULL) | 
|  | info->value = g_strdup (value); | 
|  |  | 
|  | if (embedded_annotations != NULL) | 
|  | info->annotations = embedded_annotations; | 
|  | } | 
|  |  | 
|  | static void | 
|  | g_dbus_arg_info_set (ParseData            *data, | 
|  | GDBusArgInfo         *info, | 
|  | const gchar          *name, | 
|  | const gchar          *signature, | 
|  | GDBusAnnotationInfo **annotations) | 
|  | { | 
|  | info->ref_count = 1; | 
|  |  | 
|  | /* name may be NULL - TODO: compute name? */ | 
|  | if (name != NULL) | 
|  | info->name = g_strdup (name); | 
|  |  | 
|  | if (signature != NULL) | 
|  | info->signature = g_strdup (signature); | 
|  |  | 
|  | if (annotations != NULL) | 
|  | info->annotations = annotations; | 
|  | } | 
|  |  | 
|  | static void | 
|  | g_dbus_method_info_set (ParseData            *data, | 
|  | GDBusMethodInfo      *info, | 
|  | const gchar          *name, | 
|  | GDBusArgInfo        **in_args, | 
|  | GDBusArgInfo        **out_args, | 
|  | GDBusAnnotationInfo **annotations) | 
|  | { | 
|  | info->ref_count = 1; | 
|  |  | 
|  | if (name != NULL) | 
|  | info->name = g_strdup (name); | 
|  |  | 
|  | if (in_args != NULL) | 
|  | info->in_args = in_args; | 
|  |  | 
|  | if (out_args != NULL) | 
|  | info->out_args = out_args; | 
|  |  | 
|  | if (annotations != NULL) | 
|  | info->annotations = annotations; | 
|  | } | 
|  |  | 
|  | static void | 
|  | g_dbus_signal_info_set (ParseData            *data, | 
|  | GDBusSignalInfo      *info, | 
|  | const gchar          *name, | 
|  | GDBusArgInfo        **args, | 
|  | GDBusAnnotationInfo **annotations) | 
|  | { | 
|  | info->ref_count = 1; | 
|  |  | 
|  | if (name != NULL) | 
|  | info->name = g_strdup (name); | 
|  |  | 
|  | if (args != NULL) | 
|  | info->args = args; | 
|  |  | 
|  | if (annotations != NULL) | 
|  | info->annotations = annotations; | 
|  | } | 
|  |  | 
|  | static void | 
|  | g_dbus_property_info_set (ParseData               *data, | 
|  | GDBusPropertyInfo       *info, | 
|  | const gchar             *name, | 
|  | const gchar             *signature, | 
|  | GDBusPropertyInfoFlags   flags, | 
|  | GDBusAnnotationInfo    **annotations) | 
|  | { | 
|  | info->ref_count = 1; | 
|  |  | 
|  | if (name != NULL) | 
|  | info->name = g_strdup (name); | 
|  |  | 
|  | if (flags != G_DBUS_PROPERTY_INFO_FLAGS_NONE) | 
|  | info->flags = flags; | 
|  |  | 
|  | if (signature != NULL) | 
|  | info->signature = g_strdup (signature); | 
|  |  | 
|  | if (annotations != NULL) | 
|  | info->annotations = annotations; | 
|  | } | 
|  |  | 
|  | static void | 
|  | g_dbus_interface_info_set (ParseData            *data, | 
|  | GDBusInterfaceInfo   *info, | 
|  | const gchar          *name, | 
|  | GDBusMethodInfo     **methods, | 
|  | GDBusSignalInfo     **signals, | 
|  | GDBusPropertyInfo   **properties, | 
|  | GDBusAnnotationInfo **annotations) | 
|  | { | 
|  | info->ref_count = 1; | 
|  |  | 
|  | if (name != NULL) | 
|  | info->name = g_strdup (name); | 
|  |  | 
|  | if (methods != NULL) | 
|  | info->methods = methods; | 
|  |  | 
|  | if (signals != NULL) | 
|  | info->signals = signals; | 
|  |  | 
|  | if (properties != NULL) | 
|  | info->properties = properties; | 
|  |  | 
|  | if (annotations != NULL) | 
|  | info->annotations = annotations; | 
|  | } | 
|  |  | 
|  | static void | 
|  | g_dbus_node_info_set (ParseData            *data, | 
|  | GDBusNodeInfo        *info, | 
|  | const gchar          *path, | 
|  | GDBusInterfaceInfo  **interfaces, | 
|  | GDBusNodeInfo       **nodes, | 
|  | GDBusAnnotationInfo **annotations) | 
|  | { | 
|  | info->ref_count = 1; | 
|  |  | 
|  | if (path != NULL) | 
|  | { | 
|  | info->path = g_strdup (path); | 
|  | /* TODO: relative / absolute path snafu */ | 
|  | } | 
|  |  | 
|  | if (interfaces != NULL) | 
|  | info->interfaces = interfaces; | 
|  |  | 
|  | if (nodes != NULL) | 
|  | info->nodes = nodes; | 
|  |  | 
|  | if (annotations != NULL) | 
|  | info->annotations = annotations; | 
|  | } | 
|  |  | 
|  | /* ---------------------------------------------------------------------------------------------------- */ | 
|  |  | 
|  | static void | 
|  | g_dbus_annotation_info_generate_xml (GDBusAnnotationInfo *info, | 
|  | guint                indent, | 
|  | GString             *string_builder) | 
|  | { | 
|  | gchar *tmp; | 
|  | guint n; | 
|  |  | 
|  | tmp = g_markup_printf_escaped ("%*s<annotation name=\"%s\" value=\"%s\"", | 
|  | indent, "", | 
|  | info->key, | 
|  | info->value); | 
|  | g_string_append (string_builder, tmp); | 
|  | g_free (tmp); | 
|  |  | 
|  | if (info->annotations == NULL) | 
|  | { | 
|  | g_string_append (string_builder, "/>\n"); | 
|  | } | 
|  | else | 
|  | { | 
|  | g_string_append (string_builder, ">\n"); | 
|  |  | 
|  | for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++) | 
|  | g_dbus_annotation_info_generate_xml (info->annotations[n], | 
|  | indent + 2, | 
|  | string_builder); | 
|  |  | 
|  | g_string_append_printf (string_builder, "%*s</annotation>\n", | 
|  | indent, ""); | 
|  | } | 
|  |  | 
|  | } | 
|  |  | 
|  | static void | 
|  | g_dbus_arg_info_generate_xml (GDBusArgInfo *info, | 
|  | guint         indent, | 
|  | const gchar  *extra_attributes, | 
|  | GString      *string_builder) | 
|  | { | 
|  | guint n; | 
|  |  | 
|  | g_string_append_printf (string_builder, "%*s<arg type=\"%s\"", | 
|  | indent, "", | 
|  | info->signature); | 
|  |  | 
|  | if (info->name != NULL) | 
|  | g_string_append_printf (string_builder, " name=\"%s\"", info->name); | 
|  |  | 
|  | if (extra_attributes != NULL) | 
|  | g_string_append_printf (string_builder, " %s", extra_attributes); | 
|  |  | 
|  | if (info->annotations == NULL) | 
|  | { | 
|  | g_string_append (string_builder, "/>\n"); | 
|  | } | 
|  | else | 
|  | { | 
|  | g_string_append (string_builder, ">\n"); | 
|  |  | 
|  | for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++) | 
|  | g_dbus_annotation_info_generate_xml (info->annotations[n], | 
|  | indent + 2, | 
|  | string_builder); | 
|  |  | 
|  | g_string_append_printf (string_builder, "%*s</arg>\n", indent, ""); | 
|  | } | 
|  |  | 
|  | } | 
|  |  | 
|  | static void | 
|  | g_dbus_method_info_generate_xml (GDBusMethodInfo *info, | 
|  | guint            indent, | 
|  | GString         *string_builder) | 
|  | { | 
|  | guint n; | 
|  |  | 
|  | g_string_append_printf (string_builder, "%*s<method name=\"%s\"", | 
|  | indent, "", | 
|  | info->name); | 
|  |  | 
|  | if (info->annotations == NULL && info->in_args == NULL && info->out_args == NULL) | 
|  | { | 
|  | g_string_append (string_builder, "/>\n"); | 
|  | } | 
|  | else | 
|  | { | 
|  | g_string_append (string_builder, ">\n"); | 
|  |  | 
|  | for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++) | 
|  | g_dbus_annotation_info_generate_xml (info->annotations[n], | 
|  | indent + 2, | 
|  | string_builder); | 
|  |  | 
|  | for (n = 0; info->in_args != NULL && info->in_args[n] != NULL; n++) | 
|  | g_dbus_arg_info_generate_xml (info->in_args[n], | 
|  | indent + 2, | 
|  | "direction=\"in\"", | 
|  | string_builder); | 
|  |  | 
|  | for (n = 0; info->out_args != NULL && info->out_args[n] != NULL; n++) | 
|  | g_dbus_arg_info_generate_xml (info->out_args[n], | 
|  | indent + 2, | 
|  | "direction=\"out\"", | 
|  | string_builder); | 
|  |  | 
|  | g_string_append_printf (string_builder, "%*s</method>\n", indent, ""); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | g_dbus_signal_info_generate_xml (GDBusSignalInfo *info, | 
|  | guint            indent, | 
|  | GString         *string_builder) | 
|  | { | 
|  | guint n; | 
|  |  | 
|  | g_string_append_printf (string_builder, "%*s<signal name=\"%s\"", | 
|  | indent, "", | 
|  | info->name); | 
|  |  | 
|  | if (info->annotations == NULL && info->args == NULL) | 
|  | { | 
|  | g_string_append (string_builder, "/>\n"); | 
|  | } | 
|  | else | 
|  | { | 
|  | g_string_append (string_builder, ">\n"); | 
|  |  | 
|  | for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++) | 
|  | g_dbus_annotation_info_generate_xml (info->annotations[n], | 
|  | indent + 2, | 
|  | string_builder); | 
|  |  | 
|  | for (n = 0; info->args != NULL && info->args[n] != NULL; n++) | 
|  | g_dbus_arg_info_generate_xml (info->args[n], | 
|  | indent + 2, | 
|  | NULL, | 
|  | string_builder); | 
|  |  | 
|  | g_string_append_printf (string_builder, "%*s</signal>\n", indent, ""); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | g_dbus_property_info_generate_xml (GDBusPropertyInfo *info, | 
|  | guint              indent, | 
|  | GString           *string_builder) | 
|  | { | 
|  | guint n; | 
|  | const gchar *access_string; | 
|  |  | 
|  | if ((info->flags & G_DBUS_PROPERTY_INFO_FLAGS_READABLE) && | 
|  | (info->flags & G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE)) | 
|  | { | 
|  | access_string = "readwrite"; | 
|  | } | 
|  | else if (info->flags & G_DBUS_PROPERTY_INFO_FLAGS_READABLE) | 
|  | { | 
|  | access_string = "read"; | 
|  | } | 
|  | else if (info->flags & G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE) | 
|  | { | 
|  | access_string = "write"; | 
|  | } | 
|  | else | 
|  | { | 
|  | g_assert_not_reached (); | 
|  | } | 
|  |  | 
|  | g_string_append_printf (string_builder, "%*s<property type=\"%s\" name=\"%s\" access=\"%s\"", | 
|  | indent, "", | 
|  | info->signature, | 
|  | info->name, | 
|  | access_string); | 
|  |  | 
|  | if (info->annotations == NULL) | 
|  | { | 
|  | g_string_append (string_builder, "/>\n"); | 
|  | } | 
|  | else | 
|  | { | 
|  | g_string_append (string_builder, ">\n"); | 
|  |  | 
|  | for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++) | 
|  | g_dbus_annotation_info_generate_xml (info->annotations[n], | 
|  | indent + 2, | 
|  | string_builder); | 
|  |  | 
|  | g_string_append_printf (string_builder, "%*s</property>\n", indent, ""); | 
|  | } | 
|  |  | 
|  | } | 
|  |  | 
|  | /** | 
|  | * g_dbus_interface_info_generate_xml: | 
|  | * @info: A #GDBusNodeInfo | 
|  | * @indent: Indentation level. | 
|  | * @string_builder: A #GString to to append XML data to. | 
|  | * | 
|  | * Appends an XML representation of @info (and its children) to @string_builder. | 
|  | * | 
|  | * This function is typically used for generating introspection XML | 
|  | * documents at run-time for handling the | 
|  | * `org.freedesktop.DBus.Introspectable.Introspect` | 
|  | * method. | 
|  | * | 
|  | * Since: 2.26 | 
|  | */ | 
|  | void | 
|  | g_dbus_interface_info_generate_xml (GDBusInterfaceInfo *info, | 
|  | guint               indent, | 
|  | GString            *string_builder) | 
|  | { | 
|  | guint n; | 
|  |  | 
|  | g_string_append_printf (string_builder, "%*s<interface name=\"%s\">\n", | 
|  | indent, "", | 
|  | info->name); | 
|  |  | 
|  | for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++) | 
|  | g_dbus_annotation_info_generate_xml (info->annotations[n], | 
|  | indent + 2, | 
|  | string_builder); | 
|  |  | 
|  | for (n = 0; info->methods != NULL && info->methods[n] != NULL; n++) | 
|  | g_dbus_method_info_generate_xml (info->methods[n], | 
|  | indent + 2, | 
|  | string_builder); | 
|  |  | 
|  | for (n = 0; info->signals != NULL && info->signals[n] != NULL; n++) | 
|  | g_dbus_signal_info_generate_xml (info->signals[n], | 
|  | indent + 2, | 
|  | string_builder); | 
|  |  | 
|  | for (n = 0; info->properties != NULL && info->properties[n] != NULL; n++) | 
|  | g_dbus_property_info_generate_xml (info->properties[n], | 
|  | indent + 2, | 
|  | string_builder); | 
|  |  | 
|  | g_string_append_printf (string_builder, "%*s</interface>\n", indent, ""); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * g_dbus_node_info_generate_xml: | 
|  | * @info: A #GDBusNodeInfo. | 
|  | * @indent: Indentation level. | 
|  | * @string_builder: A #GString to to append XML data to. | 
|  | * | 
|  | * Appends an XML representation of @info (and its children) to @string_builder. | 
|  | * | 
|  | * This function is typically used for generating introspection XML documents at run-time for | 
|  | * handling the `org.freedesktop.DBus.Introspectable.Introspect`  method. | 
|  | * | 
|  | * Since: 2.26 | 
|  | */ | 
|  | void | 
|  | g_dbus_node_info_generate_xml (GDBusNodeInfo *info, | 
|  | guint          indent, | 
|  | GString       *string_builder) | 
|  | { | 
|  | guint n; | 
|  |  | 
|  | g_string_append_printf (string_builder, "%*s<node", indent, ""); | 
|  | if (info->path != NULL) | 
|  | g_string_append_printf (string_builder, " name=\"%s\"", info->path); | 
|  |  | 
|  | if (info->interfaces == NULL && info->nodes == NULL && info->annotations == NULL) | 
|  | { | 
|  | g_string_append (string_builder, "/>\n"); | 
|  | } | 
|  | else | 
|  | { | 
|  | g_string_append (string_builder, ">\n"); | 
|  |  | 
|  | for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++) | 
|  | g_dbus_annotation_info_generate_xml (info->annotations[n], | 
|  | indent + 2, | 
|  | string_builder); | 
|  |  | 
|  | for (n = 0; info->interfaces != NULL && info->interfaces[n] != NULL; n++) | 
|  | g_dbus_interface_info_generate_xml (info->interfaces[n], | 
|  | indent + 2, | 
|  | string_builder); | 
|  |  | 
|  | for (n = 0; info->nodes != NULL && info->nodes[n] != NULL; n++) | 
|  | g_dbus_node_info_generate_xml (info->nodes[n], | 
|  | indent + 2, | 
|  | string_builder); | 
|  |  | 
|  | g_string_append_printf (string_builder, "%*s</node>\n", indent, ""); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* ---------------------------------------------------------------------------------------------------- */ | 
|  |  | 
|  | static GDBusAnnotationInfo ** | 
|  | parse_data_steal_annotations (ParseData *data, | 
|  | guint     *out_num_elements) | 
|  | { | 
|  | GDBusAnnotationInfo **ret; | 
|  | if (out_num_elements != NULL) | 
|  | *out_num_elements = data->annotations->len; | 
|  | if (data->annotations == NULL) | 
|  | ret = NULL; | 
|  | else | 
|  | { | 
|  | g_ptr_array_add (data->annotations, NULL); | 
|  | ret = (GDBusAnnotationInfo **) g_ptr_array_free (data->annotations, FALSE); | 
|  | } | 
|  | data->annotations = g_ptr_array_new (); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static GDBusArgInfo ** | 
|  | parse_data_steal_args (ParseData *data, | 
|  | guint     *out_num_elements) | 
|  | { | 
|  | GDBusArgInfo **ret; | 
|  | if (out_num_elements != NULL) | 
|  | *out_num_elements = data->args->len; | 
|  | if (data->args == NULL) | 
|  | ret = NULL; | 
|  | else | 
|  | { | 
|  | g_ptr_array_add (data->args, NULL); | 
|  | ret = (GDBusArgInfo **) g_ptr_array_free (data->args, FALSE); | 
|  | } | 
|  | data->args = g_ptr_array_new (); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static GDBusArgInfo ** | 
|  | parse_data_steal_out_args (ParseData *data, | 
|  | guint     *out_num_elements) | 
|  | { | 
|  | GDBusArgInfo **ret; | 
|  | if (out_num_elements != NULL) | 
|  | *out_num_elements = data->out_args->len; | 
|  | if (data->out_args == NULL) | 
|  | ret = NULL; | 
|  | else | 
|  | { | 
|  | g_ptr_array_add (data->out_args, NULL); | 
|  | ret = (GDBusArgInfo **) g_ptr_array_free (data->out_args, FALSE); | 
|  | } | 
|  | data->out_args = g_ptr_array_new (); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static GDBusMethodInfo ** | 
|  | parse_data_steal_methods (ParseData *data, | 
|  | guint     *out_num_elements) | 
|  | { | 
|  | GDBusMethodInfo **ret; | 
|  | if (out_num_elements != NULL) | 
|  | *out_num_elements = data->methods->len; | 
|  | if (data->methods == NULL) | 
|  | ret = NULL; | 
|  | else | 
|  | { | 
|  | g_ptr_array_add (data->methods, NULL); | 
|  | ret = (GDBusMethodInfo **) g_ptr_array_free (data->methods, FALSE); | 
|  | } | 
|  | data->methods = g_ptr_array_new (); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static GDBusSignalInfo ** | 
|  | parse_data_steal_signals (ParseData *data, | 
|  | guint     *out_num_elements) | 
|  | { | 
|  | GDBusSignalInfo **ret; | 
|  | if (out_num_elements != NULL) | 
|  | *out_num_elements = data->signals->len; | 
|  | if (data->signals == NULL) | 
|  | ret = NULL; | 
|  | else | 
|  | { | 
|  | g_ptr_array_add (data->signals, NULL); | 
|  | ret = (GDBusSignalInfo **) g_ptr_array_free (data->signals, FALSE); | 
|  | } | 
|  | data->signals = g_ptr_array_new (); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static GDBusPropertyInfo ** | 
|  | parse_data_steal_properties (ParseData *data, | 
|  | guint     *out_num_elements) | 
|  | { | 
|  | GDBusPropertyInfo **ret; | 
|  | if (out_num_elements != NULL) | 
|  | *out_num_elements = data->properties->len; | 
|  | if (data->properties == NULL) | 
|  | ret = NULL; | 
|  | else | 
|  | { | 
|  | g_ptr_array_add (data->properties, NULL); | 
|  | ret = (GDBusPropertyInfo **) g_ptr_array_free (data->properties, FALSE); | 
|  | } | 
|  | data->properties = g_ptr_array_new (); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static GDBusInterfaceInfo ** | 
|  | parse_data_steal_interfaces (ParseData *data, | 
|  | guint     *out_num_elements) | 
|  | { | 
|  | GDBusInterfaceInfo **ret; | 
|  | if (out_num_elements != NULL) | 
|  | *out_num_elements = data->interfaces->len; | 
|  | if (data->interfaces == NULL) | 
|  | ret = NULL; | 
|  | else | 
|  | { | 
|  | g_ptr_array_add (data->interfaces, NULL); | 
|  | ret = (GDBusInterfaceInfo **) g_ptr_array_free (data->interfaces, FALSE); | 
|  | } | 
|  | data->interfaces = g_ptr_array_new (); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static GDBusNodeInfo ** | 
|  | parse_data_steal_nodes (ParseData *data, | 
|  | guint     *out_num_elements) | 
|  | { | 
|  | GDBusNodeInfo **ret; | 
|  | if (out_num_elements != NULL) | 
|  | *out_num_elements = data->nodes->len; | 
|  | if (data->nodes == NULL) | 
|  | ret = NULL; | 
|  | else | 
|  | { | 
|  | g_ptr_array_add (data->nodes, NULL); | 
|  | ret = (GDBusNodeInfo **) g_ptr_array_free (data->nodes, FALSE); | 
|  | } | 
|  | data->nodes = g_ptr_array_new (); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* ---------------------------------------------------------------------------------------------------- */ | 
|  |  | 
|  | static void | 
|  | parse_data_free_annotations (ParseData *data) | 
|  | { | 
|  | if (data->annotations == NULL) | 
|  | return; | 
|  | g_ptr_array_foreach (data->annotations, (GFunc) g_dbus_annotation_info_unref, NULL); | 
|  | g_ptr_array_free (data->annotations, TRUE); | 
|  | data->annotations = NULL; | 
|  | } | 
|  |  | 
|  | static void | 
|  | parse_data_free_args (ParseData *data) | 
|  | { | 
|  | if (data->args == NULL) | 
|  | return; | 
|  | g_ptr_array_foreach (data->args, (GFunc) g_dbus_arg_info_unref, NULL); | 
|  | g_ptr_array_free (data->args, TRUE); | 
|  | data->args = NULL; | 
|  | } | 
|  |  | 
|  | static void | 
|  | parse_data_free_out_args (ParseData *data) | 
|  | { | 
|  | if (data->out_args == NULL) | 
|  | return; | 
|  | g_ptr_array_foreach (data->out_args, (GFunc) g_dbus_arg_info_unref, NULL); | 
|  | g_ptr_array_free (data->out_args, TRUE); | 
|  | data->out_args = NULL; | 
|  | } | 
|  |  | 
|  | static void | 
|  | parse_data_free_methods (ParseData *data) | 
|  | { | 
|  | if (data->methods == NULL) | 
|  | return; | 
|  | g_ptr_array_foreach (data->methods, (GFunc) g_dbus_method_info_unref, NULL); | 
|  | g_ptr_array_free (data->methods, TRUE); | 
|  | data->methods = NULL; | 
|  | } | 
|  |  | 
|  | static void | 
|  | parse_data_free_signals (ParseData *data) | 
|  | { | 
|  | if (data->signals == NULL) | 
|  | return; | 
|  | g_ptr_array_foreach (data->signals, (GFunc) g_dbus_signal_info_unref, NULL); | 
|  | g_ptr_array_free (data->signals, TRUE); | 
|  | data->signals = NULL; | 
|  | } | 
|  |  | 
|  | static void | 
|  | parse_data_free_properties (ParseData *data) | 
|  | { | 
|  | if (data->properties == NULL) | 
|  | return; | 
|  | g_ptr_array_foreach (data->properties, (GFunc) g_dbus_property_info_unref, NULL); | 
|  | g_ptr_array_free (data->properties, TRUE); | 
|  | data->properties = NULL; | 
|  | } | 
|  |  | 
|  | static void | 
|  | parse_data_free_interfaces (ParseData *data) | 
|  | { | 
|  | if (data->interfaces == NULL) | 
|  | return; | 
|  | g_ptr_array_foreach (data->interfaces, (GFunc) g_dbus_interface_info_unref, NULL); | 
|  | g_ptr_array_free (data->interfaces, TRUE); | 
|  | data->interfaces = NULL; | 
|  | } | 
|  |  | 
|  | static void | 
|  | parse_data_free_nodes (ParseData *data) | 
|  | { | 
|  | if (data->nodes == NULL) | 
|  | return; | 
|  | g_ptr_array_foreach (data->nodes, (GFunc) g_dbus_node_info_unref, NULL); | 
|  | g_ptr_array_free (data->nodes, TRUE); | 
|  | data->nodes = NULL; | 
|  | } | 
|  |  | 
|  | /* ---------------------------------------------------------------------------------------------------- */ | 
|  |  | 
|  | static GDBusAnnotationInfo * | 
|  | parse_data_get_annotation (ParseData *data, | 
|  | gboolean   create_new) | 
|  | { | 
|  | if (create_new) | 
|  | g_ptr_array_add (data->annotations, g_new0 (GDBusAnnotationInfo, 1)); | 
|  | return data->annotations->pdata[data->annotations->len - 1]; | 
|  | } | 
|  |  | 
|  | static GDBusArgInfo * | 
|  | parse_data_get_arg (ParseData *data, | 
|  | gboolean   create_new) | 
|  | { | 
|  | if (create_new) | 
|  | g_ptr_array_add (data->args, g_new0 (GDBusArgInfo, 1)); | 
|  | return data->args->pdata[data->args->len - 1]; | 
|  | } | 
|  |  | 
|  | static GDBusArgInfo * | 
|  | parse_data_get_out_arg (ParseData *data, | 
|  | gboolean   create_new) | 
|  | { | 
|  | if (create_new) | 
|  | g_ptr_array_add (data->out_args, g_new0 (GDBusArgInfo, 1)); | 
|  | return data->out_args->pdata[data->out_args->len - 1]; | 
|  | } | 
|  |  | 
|  | static GDBusMethodInfo * | 
|  | parse_data_get_method (ParseData *data, | 
|  | gboolean   create_new) | 
|  | { | 
|  | if (create_new) | 
|  | g_ptr_array_add (data->methods, g_new0 (GDBusMethodInfo, 1)); | 
|  | return data->methods->pdata[data->methods->len - 1]; | 
|  | } | 
|  |  | 
|  | static GDBusSignalInfo * | 
|  | parse_data_get_signal (ParseData *data, | 
|  | gboolean   create_new) | 
|  | { | 
|  | if (create_new) | 
|  | g_ptr_array_add (data->signals, g_new0 (GDBusSignalInfo, 1)); | 
|  | return data->signals->pdata[data->signals->len - 1]; | 
|  | } | 
|  |  | 
|  | static GDBusPropertyInfo * | 
|  | parse_data_get_property (ParseData *data, | 
|  | gboolean   create_new) | 
|  | { | 
|  | if (create_new) | 
|  | g_ptr_array_add (data->properties, g_new0 (GDBusPropertyInfo, 1)); | 
|  | return data->properties->pdata[data->properties->len - 1]; | 
|  | } | 
|  |  | 
|  | static GDBusInterfaceInfo * | 
|  | parse_data_get_interface (ParseData *data, | 
|  | gboolean   create_new) | 
|  | { | 
|  | if (create_new) | 
|  | g_ptr_array_add (data->interfaces, g_new0 (GDBusInterfaceInfo, 1)); | 
|  | return data->interfaces->pdata[data->interfaces->len - 1]; | 
|  | } | 
|  |  | 
|  | static GDBusNodeInfo * | 
|  | parse_data_get_node (ParseData *data, | 
|  | gboolean   create_new) | 
|  | { | 
|  | if (create_new) | 
|  | g_ptr_array_add (data->nodes, g_new0 (GDBusNodeInfo, 1)); | 
|  | return data->nodes->pdata[data->nodes->len - 1]; | 
|  | } | 
|  |  | 
|  | /* ---------------------------------------------------------------------------------------------------- */ | 
|  |  | 
|  | static ParseData * | 
|  | parse_data_new (void) | 
|  | { | 
|  | ParseData *data; | 
|  |  | 
|  | data = g_new0 (ParseData, 1); | 
|  |  | 
|  | /* initialize arrays */ | 
|  | parse_data_steal_annotations (data, NULL); | 
|  | parse_data_steal_args (data, NULL); | 
|  | parse_data_steal_out_args (data, NULL); | 
|  | parse_data_steal_methods (data, NULL); | 
|  | parse_data_steal_signals (data, NULL); | 
|  | parse_data_steal_properties (data, NULL); | 
|  | parse_data_steal_interfaces (data, NULL); | 
|  | parse_data_steal_nodes (data, NULL); | 
|  |  | 
|  | return data; | 
|  | } | 
|  |  | 
|  | static void | 
|  | parse_data_free (ParseData *data) | 
|  | { | 
|  | GSList *l; | 
|  |  | 
|  | /* free stack of annotation arrays */ | 
|  | for (l = data->annotations_stack; l != NULL; l = l->next) | 
|  | { | 
|  | GPtrArray *annotations = l->data; | 
|  | g_ptr_array_foreach (annotations, (GFunc) g_dbus_annotation_info_unref, NULL); | 
|  | g_ptr_array_free (annotations, TRUE); | 
|  | } | 
|  | g_slist_free (data->annotations_stack); | 
|  |  | 
|  | /* free stack of interface arrays */ | 
|  | for (l = data->interfaces_stack; l != NULL; l = l->next) | 
|  | { | 
|  | GPtrArray *interfaces = l->data; | 
|  | g_ptr_array_foreach (interfaces, (GFunc) g_dbus_interface_info_unref, NULL); | 
|  | g_ptr_array_free (interfaces, TRUE); | 
|  | } | 
|  | g_slist_free (data->interfaces_stack); | 
|  |  | 
|  | /* free stack of node arrays */ | 
|  | for (l = data->nodes_stack; l != NULL; l = l->next) | 
|  | { | 
|  | GPtrArray *nodes = l->data; | 
|  | g_ptr_array_foreach (nodes, (GFunc) g_dbus_node_info_unref, NULL); | 
|  | g_ptr_array_free (nodes, TRUE); | 
|  | } | 
|  | g_slist_free (data->nodes_stack); | 
|  |  | 
|  | /* free arrays (data->annotations, data->interfaces and data->nodes have been freed above) */ | 
|  | parse_data_free_args (data); | 
|  | parse_data_free_out_args (data); | 
|  | parse_data_free_methods (data); | 
|  | parse_data_free_signals (data); | 
|  | parse_data_free_properties (data); | 
|  | parse_data_free_interfaces (data); | 
|  | parse_data_free_annotations (data); | 
|  | parse_data_free_nodes (data); | 
|  |  | 
|  | g_free (data); | 
|  | } | 
|  |  | 
|  | /* ---------------------------------------------------------------------------------------------------- */ | 
|  |  | 
|  | static void | 
|  | parser_start_element (GMarkupParseContext  *context, | 
|  | const gchar          *element_name, | 
|  | const gchar         **attribute_names, | 
|  | const gchar         **attribute_values, | 
|  | gpointer              user_data, | 
|  | GError              **error) | 
|  | { | 
|  | ParseData *data = user_data; | 
|  | GSList *stack; | 
|  | const gchar *name; | 
|  | const gchar *type; | 
|  | const gchar *access; | 
|  | const gchar *direction; | 
|  | const gchar *value; | 
|  |  | 
|  | name = NULL; | 
|  | type = NULL; | 
|  | access = NULL; | 
|  | direction = NULL; | 
|  | value = NULL; | 
|  |  | 
|  | stack = (GSList *) g_markup_parse_context_get_element_stack (context); | 
|  |  | 
|  | /* ---------------------------------------------------------------------------------------------------- */ | 
|  | if (strcmp (element_name, "node") == 0) | 
|  | { | 
|  | if (!(g_slist_length (stack) >= 1 || strcmp (stack->next->data, "node") != 0)) | 
|  | { | 
|  | g_set_error_literal (error, | 
|  | G_MARKUP_ERROR, | 
|  | G_MARKUP_ERROR_INVALID_CONTENT, | 
|  | "<node> elements can only be top-level or embedded in other <node> elements"); | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | if (!g_markup_collect_attributes (element_name, | 
|  | attribute_names, | 
|  | attribute_values, | 
|  | error, | 
|  | G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "name", &name, | 
|  | /* some hand-written introspection XML documents use this */ | 
|  | G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "xmlns:doc", NULL, | 
|  | G_MARKUP_COLLECT_INVALID)) | 
|  | goto out; | 
|  |  | 
|  | g_dbus_node_info_set (data, | 
|  | parse_data_get_node (data, TRUE), | 
|  | name, | 
|  | NULL, | 
|  | NULL, | 
|  | NULL); | 
|  |  | 
|  | /* push the currently retrieved interfaces and nodes on the stack and prepare new arrays */ | 
|  | data->interfaces_stack = g_slist_prepend (data->interfaces_stack, data->interfaces); | 
|  | data->interfaces = NULL; | 
|  | parse_data_steal_interfaces (data, NULL); | 
|  |  | 
|  | data->nodes_stack = g_slist_prepend (data->nodes_stack, data->nodes); | 
|  | data->nodes = NULL; | 
|  | parse_data_steal_nodes (data, NULL); | 
|  |  | 
|  | } | 
|  | /* ---------------------------------------------------------------------------------------------------- */ | 
|  | else if (strcmp (element_name, "interface") == 0) | 
|  | { | 
|  | if (g_slist_length (stack) < 2 || strcmp (stack->next->data, "node") != 0) | 
|  | { | 
|  | g_set_error_literal (error, | 
|  | G_MARKUP_ERROR, | 
|  | G_MARKUP_ERROR_INVALID_CONTENT, | 
|  | "<interface> elements can only be embedded in <node> elements"); | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | if (!g_markup_collect_attributes (element_name, | 
|  | attribute_names, | 
|  | attribute_values, | 
|  | error, | 
|  | G_MARKUP_COLLECT_STRING, "name", &name, | 
|  | /* seen in the wild */ | 
|  | G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "version", NULL, | 
|  | G_MARKUP_COLLECT_INVALID)) | 
|  | goto out; | 
|  |  | 
|  | g_dbus_interface_info_set (data, | 
|  | parse_data_get_interface (data, TRUE), | 
|  | name, | 
|  | NULL, | 
|  | NULL, | 
|  | NULL, | 
|  | NULL); | 
|  |  | 
|  | } | 
|  | /* ---------------------------------------------------------------------------------------------------- */ | 
|  | else if (strcmp (element_name, "method") == 0) | 
|  | { | 
|  | if (g_slist_length (stack) < 2 || strcmp (stack->next->data, "interface") != 0) | 
|  | { | 
|  | g_set_error_literal (error, | 
|  | G_MARKUP_ERROR, | 
|  | G_MARKUP_ERROR_INVALID_CONTENT, | 
|  | "<method> elements can only be embedded in <interface> elements"); | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | if (!g_markup_collect_attributes (element_name, | 
|  | attribute_names, | 
|  | attribute_values, | 
|  | error, | 
|  | G_MARKUP_COLLECT_STRING, "name", &name, | 
|  | /* seen in the wild */ | 
|  | G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "version", NULL, | 
|  | G_MARKUP_COLLECT_INVALID)) | 
|  | goto out; | 
|  |  | 
|  | g_dbus_method_info_set (data, | 
|  | parse_data_get_method (data, TRUE), | 
|  | name, | 
|  | NULL, | 
|  | NULL, | 
|  | NULL); | 
|  |  | 
|  | data->num_args = 0; | 
|  |  | 
|  | } | 
|  | /* ---------------------------------------------------------------------------------------------------- */ | 
|  | else if (strcmp (element_name, "signal") == 0) | 
|  | { | 
|  | if (g_slist_length (stack) < 2 || strcmp (stack->next->data, "interface") != 0) | 
|  | { | 
|  | g_set_error_literal (error, | 
|  | G_MARKUP_ERROR, | 
|  | G_MARKUP_ERROR_INVALID_CONTENT, | 
|  | "<signal> elements can only be embedded in <interface> elements"); | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | if (!g_markup_collect_attributes (element_name, | 
|  | attribute_names, | 
|  | attribute_values, | 
|  | error, | 
|  | G_MARKUP_COLLECT_STRING, "name", &name, | 
|  | G_MARKUP_COLLECT_INVALID)) | 
|  | goto out; | 
|  |  | 
|  | g_dbus_signal_info_set (data, | 
|  | parse_data_get_signal (data, TRUE), | 
|  | name, | 
|  | NULL, | 
|  | NULL); | 
|  |  | 
|  | data->num_args = 0; | 
|  |  | 
|  | } | 
|  | /* ---------------------------------------------------------------------------------------------------- */ | 
|  | else if (strcmp (element_name, "property") == 0) | 
|  | { | 
|  | GDBusPropertyInfoFlags flags; | 
|  |  | 
|  | if (g_slist_length (stack) < 2 || strcmp (stack->next->data, "interface") != 0) | 
|  | { | 
|  | g_set_error_literal (error, | 
|  | G_MARKUP_ERROR, | 
|  | G_MARKUP_ERROR_INVALID_CONTENT, | 
|  | "<property> elements can only be embedded in <interface> elements"); | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | if (!g_markup_collect_attributes (element_name, | 
|  | attribute_names, | 
|  | attribute_values, | 
|  | error, | 
|  | G_MARKUP_COLLECT_STRING, "name", &name, | 
|  | G_MARKUP_COLLECT_STRING, "type", &type, | 
|  | G_MARKUP_COLLECT_STRING, "access", &access, | 
|  | G_MARKUP_COLLECT_INVALID)) | 
|  | goto out; | 
|  |  | 
|  | if (strcmp (access, "read") == 0) | 
|  | flags = G_DBUS_PROPERTY_INFO_FLAGS_READABLE; | 
|  | else if (strcmp (access, "write") == 0) | 
|  | flags = G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE; | 
|  | else if (strcmp (access, "readwrite") == 0) | 
|  | flags = G_DBUS_PROPERTY_INFO_FLAGS_READABLE | G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE; | 
|  | else | 
|  | { | 
|  | g_set_error (error, | 
|  | G_MARKUP_ERROR, | 
|  | G_MARKUP_ERROR_INVALID_CONTENT, | 
|  | "Unknown value '%s' of access attribute for element <property>", | 
|  | access); | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | g_dbus_property_info_set (data, | 
|  | parse_data_get_property (data, TRUE), | 
|  | name, | 
|  | type, | 
|  | flags, | 
|  | NULL); | 
|  |  | 
|  | } | 
|  | /* ---------------------------------------------------------------------------------------------------- */ | 
|  | else if (strcmp (element_name, "arg") == 0) | 
|  | { | 
|  | gboolean is_in; | 
|  | gchar *name_to_use; | 
|  |  | 
|  | if (g_slist_length (stack) < 2 || | 
|  | (strcmp (stack->next->data, "method") != 0 && | 
|  | strcmp (stack->next->data, "signal") != 0)) | 
|  | { | 
|  | g_set_error_literal (error, | 
|  | G_MARKUP_ERROR, | 
|  | G_MARKUP_ERROR_INVALID_CONTENT, | 
|  | "<arg> elements can only be embedded in <method> or <signal> elements"); | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | if (!g_markup_collect_attributes (element_name, | 
|  | attribute_names, | 
|  | attribute_values, | 
|  | error, | 
|  | G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "name", &name, | 
|  | G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "direction", &direction, | 
|  | G_MARKUP_COLLECT_STRING, "type", &type, | 
|  | G_MARKUP_COLLECT_INVALID)) | 
|  | goto out; | 
|  |  | 
|  | if (strcmp (stack->next->data, "method") == 0) | 
|  | is_in = TRUE; | 
|  | else | 
|  | is_in = FALSE; | 
|  | if (direction != NULL) | 
|  | { | 
|  | if (strcmp (direction, "in") == 0) | 
|  | is_in = TRUE; | 
|  | else if (strcmp (direction, "out") == 0) | 
|  | is_in = FALSE; | 
|  | else | 
|  | { | 
|  | g_set_error (error, | 
|  | G_MARKUP_ERROR, | 
|  | G_MARKUP_ERROR_INVALID_CONTENT, | 
|  | "Unknown value '%s' of direction attribute", | 
|  | direction); | 
|  | goto out; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (is_in && strcmp (stack->next->data, "signal") == 0) | 
|  | { | 
|  | g_set_error_literal (error, | 
|  | G_MARKUP_ERROR, | 
|  | G_MARKUP_ERROR_INVALID_CONTENT, | 
|  | "Only direction 'out' is allowed for <arg> elements embedded in <signal>"); | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | if (name == NULL) | 
|  | name_to_use = g_strdup_printf ("arg_%d", data->num_args); | 
|  | else | 
|  | name_to_use = g_strdup (name); | 
|  | data->num_args++; | 
|  |  | 
|  | if (is_in) | 
|  | { | 
|  | g_dbus_arg_info_set (data, | 
|  | parse_data_get_arg (data, TRUE), | 
|  | name_to_use, | 
|  | type, | 
|  | NULL); | 
|  | data->last_arg_was_in = TRUE; | 
|  | } | 
|  | else | 
|  | { | 
|  | g_dbus_arg_info_set (data, | 
|  | parse_data_get_out_arg (data, TRUE), | 
|  | name_to_use, | 
|  | type, | 
|  | NULL); | 
|  | data->last_arg_was_in = FALSE; | 
|  |  | 
|  | } | 
|  |  | 
|  | g_free (name_to_use); | 
|  | } | 
|  | /* ---------------------------------------------------------------------------------------------------- */ | 
|  | else if (strcmp (element_name, "annotation") == 0) | 
|  | { | 
|  | if (g_slist_length (stack) < 2 || | 
|  | (strcmp (stack->next->data, "node") != 0 && | 
|  | strcmp (stack->next->data, "interface") != 0 && | 
|  | strcmp (stack->next->data, "signal") != 0 && | 
|  | strcmp (stack->next->data, "method") != 0 && | 
|  | strcmp (stack->next->data, "property") != 0 && | 
|  | strcmp (stack->next->data, "arg") != 0 && | 
|  | strcmp (stack->next->data, "annotation") != 0)) | 
|  | { | 
|  | g_set_error_literal (error, | 
|  | G_MARKUP_ERROR, | 
|  | G_MARKUP_ERROR_INVALID_CONTENT, | 
|  | "<annotation> elements can only be embedded in <node>, <interface>, <signal>, <method>, <property>, <arg> or <annotation> elements"); | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | if (!g_markup_collect_attributes (element_name, | 
|  | attribute_names, | 
|  | attribute_values, | 
|  | error, | 
|  | G_MARKUP_COLLECT_STRING, "name", &name, | 
|  | G_MARKUP_COLLECT_STRING, "value", &value, | 
|  | G_MARKUP_COLLECT_INVALID)) | 
|  | goto out; | 
|  |  | 
|  | g_dbus_annotation_info_set (data, | 
|  | parse_data_get_annotation (data, TRUE), | 
|  | name, | 
|  | value, | 
|  | NULL); | 
|  | } | 
|  | /* ---------------------------------------------------------------------------------------------------- */ | 
|  | else | 
|  | { | 
|  | /* don't bail on unknown elements; just ignore them */ | 
|  | } | 
|  | /* ---------------------------------------------------------------------------------------------------- */ | 
|  |  | 
|  | /* push the currently retrieved annotations on the stack and prepare a new one */ | 
|  | data->annotations_stack = g_slist_prepend (data->annotations_stack, data->annotations); | 
|  | data->annotations = NULL; | 
|  | parse_data_steal_annotations (data, NULL); | 
|  |  | 
|  | out: | 
|  | ; | 
|  | } | 
|  |  | 
|  | /* ---------------------------------------------------------------------------------------------------- */ | 
|  |  | 
|  | static GDBusAnnotationInfo ** | 
|  | steal_annotations (ParseData *data) | 
|  | { | 
|  | return parse_data_steal_annotations (data, NULL); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void | 
|  | parser_end_element (GMarkupParseContext  *context, | 
|  | const gchar          *element_name, | 
|  | gpointer              user_data, | 
|  | GError              **error) | 
|  | { | 
|  | ParseData *data = user_data; | 
|  | gboolean have_popped_annotations; | 
|  |  | 
|  | have_popped_annotations = FALSE; | 
|  |  | 
|  | if (strcmp (element_name, "node") == 0) | 
|  | { | 
|  | guint num_nodes; | 
|  | guint num_interfaces; | 
|  | GDBusNodeInfo **nodes; | 
|  | GDBusInterfaceInfo **interfaces; | 
|  |  | 
|  | nodes = parse_data_steal_nodes (data, &num_nodes); | 
|  | interfaces = parse_data_steal_interfaces (data, &num_interfaces); | 
|  |  | 
|  | /* destroy the nodes, interfaces for scope we're exiting and pop the nodes, interfaces from the | 
|  | * scope we're reentering | 
|  | */ | 
|  | parse_data_free_interfaces (data); | 
|  | data->interfaces = (GPtrArray *) data->interfaces_stack->data; | 
|  | data->interfaces_stack = g_slist_remove (data->interfaces_stack, data->interfaces_stack->data); | 
|  |  | 
|  | parse_data_free_nodes (data); | 
|  | data->nodes = (GPtrArray *) data->nodes_stack->data; | 
|  | data->nodes_stack = g_slist_remove (data->nodes_stack, data->nodes_stack->data); | 
|  |  | 
|  | g_dbus_node_info_set (data, | 
|  | parse_data_get_node (data, FALSE), | 
|  | NULL, | 
|  | interfaces, | 
|  | nodes, | 
|  | steal_annotations (data)); | 
|  |  | 
|  | } | 
|  | else if (strcmp (element_name, "interface") == 0) | 
|  | { | 
|  | guint num_methods; | 
|  | guint num_signals; | 
|  | guint num_properties; | 
|  | GDBusMethodInfo **methods; | 
|  | GDBusSignalInfo **signals; | 
|  | GDBusPropertyInfo **properties; | 
|  |  | 
|  | methods    = parse_data_steal_methods    (data, &num_methods); | 
|  | signals    = parse_data_steal_signals    (data, &num_signals); | 
|  | properties = parse_data_steal_properties (data, &num_properties); | 
|  |  | 
|  | g_dbus_interface_info_set (data, | 
|  | parse_data_get_interface (data, FALSE), | 
|  | NULL, | 
|  | methods, | 
|  | signals, | 
|  | properties, | 
|  | steal_annotations (data)); | 
|  |  | 
|  | } | 
|  | else if (strcmp (element_name, "method") == 0) | 
|  | { | 
|  | guint in_num_args; | 
|  | guint out_num_args; | 
|  | GDBusArgInfo **in_args; | 
|  | GDBusArgInfo **out_args; | 
|  |  | 
|  | in_args  = parse_data_steal_args     (data, &in_num_args); | 
|  | out_args = parse_data_steal_out_args (data, &out_num_args); | 
|  |  | 
|  | g_dbus_method_info_set (data, | 
|  | parse_data_get_method (data, FALSE), | 
|  | NULL, | 
|  | in_args, | 
|  | out_args, | 
|  | steal_annotations (data)); | 
|  | } | 
|  | else if (strcmp (element_name, "signal") == 0) | 
|  | { | 
|  | guint num_args; | 
|  | GDBusArgInfo **args; | 
|  |  | 
|  | args = parse_data_steal_out_args (data, &num_args); | 
|  |  | 
|  | g_dbus_signal_info_set (data, | 
|  | parse_data_get_signal (data, FALSE), | 
|  | NULL, | 
|  | args, | 
|  | steal_annotations (data)); | 
|  | } | 
|  | else if (strcmp (element_name, "property") == 0) | 
|  | { | 
|  | g_dbus_property_info_set (data, | 
|  | parse_data_get_property (data, FALSE), | 
|  | NULL, | 
|  | NULL, | 
|  | G_DBUS_PROPERTY_INFO_FLAGS_NONE, | 
|  | steal_annotations (data)); | 
|  | } | 
|  | else if (strcmp (element_name, "arg") == 0) | 
|  | { | 
|  | g_dbus_arg_info_set (data, | 
|  | data->last_arg_was_in ? parse_data_get_arg (data, FALSE) : parse_data_get_out_arg (data, FALSE), | 
|  | NULL, | 
|  | NULL, | 
|  | steal_annotations (data)); | 
|  | } | 
|  | else if (strcmp (element_name, "annotation") == 0) | 
|  | { | 
|  | GDBusAnnotationInfo **embedded_annotations; | 
|  |  | 
|  | embedded_annotations = steal_annotations (data); | 
|  |  | 
|  | /* destroy the annotations for scope we're exiting and pop the annotations from the scope we're reentering */ | 
|  | parse_data_free_annotations (data); | 
|  | data->annotations = (GPtrArray *) data->annotations_stack->data; | 
|  | data->annotations_stack = g_slist_remove (data->annotations_stack, data->annotations_stack->data); | 
|  |  | 
|  | have_popped_annotations = TRUE; | 
|  |  | 
|  | g_dbus_annotation_info_set (data, | 
|  | parse_data_get_annotation (data, FALSE), | 
|  | NULL, | 
|  | NULL, | 
|  | embedded_annotations); | 
|  | } | 
|  | else | 
|  | { | 
|  | /* don't bail on unknown elements; just ignore them */ | 
|  | } | 
|  |  | 
|  | if (!have_popped_annotations) | 
|  | { | 
|  | /* destroy the annotations for scope we're exiting and pop the annotations from the scope we're reentering */ | 
|  | parse_data_free_annotations (data); | 
|  | data->annotations = (GPtrArray *) data->annotations_stack->data; | 
|  | data->annotations_stack = g_slist_remove (data->annotations_stack, data->annotations_stack->data); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* ---------------------------------------------------------------------------------------------------- */ | 
|  |  | 
|  | static void | 
|  | parser_error (GMarkupParseContext *context, | 
|  | GError              *error, | 
|  | gpointer             user_data) | 
|  | { | 
|  | gint line_number; | 
|  | gint char_number; | 
|  |  | 
|  | g_markup_parse_context_get_position (context, &line_number, &char_number); | 
|  |  | 
|  | g_prefix_error (&error, "%d:%d: ", | 
|  | line_number, | 
|  | char_number); | 
|  | } | 
|  |  | 
|  | /* ---------------------------------------------------------------------------------------------------- */ | 
|  |  | 
|  | /** | 
|  | * g_dbus_node_info_new_for_xml: | 
|  | * @xml_data: Valid D-Bus introspection XML. | 
|  | * @error: Return location for error. | 
|  | * | 
|  | * Parses @xml_data and returns a #GDBusNodeInfo representing the data. | 
|  | * | 
|  | * The introspection XML must contain exactly one top-level | 
|  | * <node> element. | 
|  | * | 
|  | * Note that this routine is using a | 
|  | * [GMarkup][glib-Simple-XML-Subset-Parser.description]-based | 
|  | * parser that only accepts a subset of valid XML documents. | 
|  | * | 
|  | * Returns: A #GDBusNodeInfo structure or %NULL if @error is set. Free | 
|  | * with g_dbus_node_info_unref(). | 
|  | * | 
|  | * Since: 2.26 | 
|  | */ | 
|  | GDBusNodeInfo * | 
|  | g_dbus_node_info_new_for_xml (const gchar  *xml_data, | 
|  | GError      **error) | 
|  | { | 
|  | GDBusNodeInfo *ret; | 
|  | GMarkupParseContext *context; | 
|  | GMarkupParser *parser; | 
|  | guint num_nodes; | 
|  | ParseData *data; | 
|  | GDBusNodeInfo **ughret; | 
|  |  | 
|  | ret = NULL; | 
|  | parser = NULL; | 
|  | context = NULL; | 
|  |  | 
|  | parser = g_new0 (GMarkupParser, 1); | 
|  | parser->start_element = parser_start_element; | 
|  | parser->end_element   = parser_end_element; | 
|  | parser->error         = parser_error; | 
|  |  | 
|  | data = parse_data_new (); | 
|  | context = g_markup_parse_context_new (parser, | 
|  | G_MARKUP_IGNORE_QUALIFIED, | 
|  | data, | 
|  | (GDestroyNotify) parse_data_free); | 
|  |  | 
|  | if (!g_markup_parse_context_parse (context, | 
|  | xml_data, | 
|  | strlen (xml_data), | 
|  | error)) | 
|  | goto out; | 
|  |  | 
|  | if (!g_markup_parse_context_end_parse (context, error)) | 
|  | goto out; | 
|  |  | 
|  | ughret = parse_data_steal_nodes (data, &num_nodes); | 
|  |  | 
|  | if (num_nodes != 1) | 
|  | { | 
|  | guint n; | 
|  |  | 
|  | g_set_error (error, | 
|  | G_MARKUP_ERROR, | 
|  | G_MARKUP_ERROR_INVALID_CONTENT, | 
|  | "Expected a single node in introspection XML, found %d", | 
|  | num_nodes); | 
|  |  | 
|  | /* clean up */ | 
|  | for (n = 0; n < num_nodes; n++) | 
|  | { | 
|  | g_dbus_node_info_unref (ughret[n]); | 
|  | ughret[n] = NULL; | 
|  | } | 
|  | } | 
|  |  | 
|  | ret = ughret[0]; | 
|  | g_free (ughret); | 
|  |  | 
|  | out: | 
|  | g_free (parser); | 
|  | if (context != NULL) | 
|  | g_markup_parse_context_free (context); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* ---------------------------------------------------------------------------------------------------- */ | 
|  |  | 
|  | /** | 
|  | * g_dbus_annotation_info_lookup: | 
|  | * @annotations: (array zero-terminated=1) (nullable): A %NULL-terminated array of annotations or %NULL. | 
|  | * @name: The name of the annotation to look up. | 
|  | * | 
|  | * Looks up the value of an annotation. | 
|  | * | 
|  | * The cost of this function is O(n) in number of annotations. | 
|  | * | 
|  | * Returns: (nullable): The value or %NULL if not found. Do not free, it is owned by @annotations. | 
|  | * | 
|  | * Since: 2.26 | 
|  | */ | 
|  | const gchar * | 
|  | g_dbus_annotation_info_lookup (GDBusAnnotationInfo **annotations, | 
|  | const gchar          *name) | 
|  | { | 
|  | guint n; | 
|  | const gchar *ret; | 
|  |  | 
|  | ret = NULL; | 
|  | for (n = 0; annotations != NULL && annotations[n] != NULL; n++) | 
|  | { | 
|  | if (g_strcmp0 (annotations[n]->key, name) == 0) | 
|  | { | 
|  | ret = annotations[n]->value; | 
|  | goto out; | 
|  | } | 
|  | } | 
|  |  | 
|  | out: | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* ---------------------------------------------------------------------------------------------------- */ | 
|  |  | 
|  | G_LOCK_DEFINE_STATIC (info_cache_lock); | 
|  |  | 
|  | typedef struct | 
|  | { | 
|  | gint use_count; | 
|  |  | 
|  | /* gchar* -> GDBusMethodInfo* */ | 
|  | GHashTable *method_name_to_data; | 
|  |  | 
|  | /* gchar* -> GDBusMethodInfo* */ | 
|  | GHashTable *signal_name_to_data; | 
|  |  | 
|  | /* gchar* -> GDBusMethodInfo* */ | 
|  | GHashTable *property_name_to_data; | 
|  | } InfoCacheEntry; | 
|  |  | 
|  | static void | 
|  | info_cache_free (InfoCacheEntry *cache) | 
|  | { | 
|  | g_assert (cache->use_count == 0); | 
|  | g_hash_table_unref (cache->method_name_to_data); | 
|  | g_hash_table_unref (cache->signal_name_to_data); | 
|  | g_hash_table_unref (cache->property_name_to_data); | 
|  | g_slice_free (InfoCacheEntry, cache); | 
|  | } | 
|  |  | 
|  | /* maps from GDBusInterfaceInfo* to InfoCacheEntry* */ | 
|  | static GHashTable *info_cache = NULL; | 
|  |  | 
|  | /* ---------------------------------------------------------------------------------------------------- */ | 
|  |  | 
|  | /** | 
|  | * g_dbus_interface_info_lookup_method: | 
|  | * @info: A #GDBusInterfaceInfo. | 
|  | * @name: A D-Bus method name (typically in CamelCase) | 
|  | * | 
|  | * Looks up information about a method. | 
|  | * | 
|  | * The cost of this function is O(n) in number of methods unless | 
|  | * g_dbus_interface_info_cache_build() has been used on @info. | 
|  | * | 
|  | * Returns: (nullable) (transfer none): A #GDBusMethodInfo or %NULL if not found. Do not free, it is owned by @info. | 
|  | * | 
|  | * Since: 2.26 | 
|  | */ | 
|  | GDBusMethodInfo * | 
|  | g_dbus_interface_info_lookup_method (GDBusInterfaceInfo *info, | 
|  | const gchar        *name) | 
|  | { | 
|  | guint n; | 
|  | GDBusMethodInfo *result; | 
|  |  | 
|  | G_LOCK (info_cache_lock); | 
|  | if (G_LIKELY (info_cache != NULL)) | 
|  | { | 
|  | InfoCacheEntry *cache; | 
|  | cache = g_hash_table_lookup (info_cache, info); | 
|  | if (G_LIKELY (cache != NULL)) | 
|  | { | 
|  | result = g_hash_table_lookup (cache->method_name_to_data, name); | 
|  | G_UNLOCK (info_cache_lock); | 
|  | goto out; | 
|  | } | 
|  | } | 
|  | G_UNLOCK (info_cache_lock); | 
|  |  | 
|  | for (n = 0; info->methods != NULL && info->methods[n] != NULL; n++) | 
|  | { | 
|  | GDBusMethodInfo *i = info->methods[n]; | 
|  |  | 
|  | if (g_strcmp0 (i->name, name) == 0) | 
|  | { | 
|  | result = i; | 
|  | goto out; | 
|  | } | 
|  | } | 
|  |  | 
|  | result = NULL; | 
|  |  | 
|  | out: | 
|  | return result; | 
|  | } | 
|  |  | 
|  | /* ---------------------------------------------------------------------------------------------------- */ | 
|  |  | 
|  | /** | 
|  | * g_dbus_interface_info_lookup_signal: | 
|  | * @info: A #GDBusInterfaceInfo. | 
|  | * @name: A D-Bus signal name (typically in CamelCase) | 
|  | * | 
|  | * Looks up information about a signal. | 
|  | * | 
|  | * The cost of this function is O(n) in number of signals unless | 
|  | * g_dbus_interface_info_cache_build() has been used on @info. | 
|  | * | 
|  | * Returns: (nullable) (transfer none): A #GDBusSignalInfo or %NULL if not found. Do not free, it is owned by @info. | 
|  | * | 
|  | * Since: 2.26 | 
|  | */ | 
|  | GDBusSignalInfo * | 
|  | g_dbus_interface_info_lookup_signal (GDBusInterfaceInfo *info, | 
|  | const gchar        *name) | 
|  | { | 
|  | guint n; | 
|  | GDBusSignalInfo *result; | 
|  |  | 
|  | G_LOCK (info_cache_lock); | 
|  | if (G_LIKELY (info_cache != NULL)) | 
|  | { | 
|  | InfoCacheEntry *cache; | 
|  | cache = g_hash_table_lookup (info_cache, info); | 
|  | if (G_LIKELY (cache != NULL)) | 
|  | { | 
|  | result = g_hash_table_lookup (cache->signal_name_to_data, name); | 
|  | G_UNLOCK (info_cache_lock); | 
|  | goto out; | 
|  | } | 
|  | } | 
|  | G_UNLOCK (info_cache_lock); | 
|  |  | 
|  | for (n = 0; info->signals != NULL && info->signals[n] != NULL; n++) | 
|  | { | 
|  | GDBusSignalInfo *i = info->signals[n]; | 
|  |  | 
|  | if (g_strcmp0 (i->name, name) == 0) | 
|  | { | 
|  | result = i; | 
|  | goto out; | 
|  | } | 
|  | } | 
|  |  | 
|  | result = NULL; | 
|  |  | 
|  | out: | 
|  | return result; | 
|  | } | 
|  |  | 
|  | /* ---------------------------------------------------------------------------------------------------- */ | 
|  |  | 
|  | /** | 
|  | * g_dbus_interface_info_lookup_property: | 
|  | * @info: A #GDBusInterfaceInfo. | 
|  | * @name: A D-Bus property name (typically in CamelCase). | 
|  | * | 
|  | * Looks up information about a property. | 
|  | * | 
|  | * The cost of this function is O(n) in number of properties unless | 
|  | * g_dbus_interface_info_cache_build() has been used on @info. | 
|  | * | 
|  | * Returns: (nullable) (transfer none): A #GDBusPropertyInfo or %NULL if not found. Do not free, it is owned by @info. | 
|  | * | 
|  | * Since: 2.26 | 
|  | */ | 
|  | GDBusPropertyInfo * | 
|  | g_dbus_interface_info_lookup_property (GDBusInterfaceInfo *info, | 
|  | const gchar        *name) | 
|  | { | 
|  | guint n; | 
|  | GDBusPropertyInfo *result; | 
|  |  | 
|  | G_LOCK (info_cache_lock); | 
|  | if (G_LIKELY (info_cache != NULL)) | 
|  | { | 
|  | InfoCacheEntry *cache; | 
|  | cache = g_hash_table_lookup (info_cache, info); | 
|  | if (G_LIKELY (cache != NULL)) | 
|  | { | 
|  | result = g_hash_table_lookup (cache->property_name_to_data, name); | 
|  | G_UNLOCK (info_cache_lock); | 
|  | goto out; | 
|  | } | 
|  | } | 
|  | G_UNLOCK (info_cache_lock); | 
|  |  | 
|  | for (n = 0; info->properties != NULL && info->properties[n] != NULL; n++) | 
|  | { | 
|  | GDBusPropertyInfo *i = info->properties[n]; | 
|  |  | 
|  | if (g_strcmp0 (i->name, name) == 0) | 
|  | { | 
|  | result = i; | 
|  | goto out; | 
|  | } | 
|  | } | 
|  |  | 
|  | result = NULL; | 
|  |  | 
|  | out: | 
|  | return result; | 
|  | } | 
|  |  | 
|  | /* ---------------------------------------------------------------------------------------------------- */ | 
|  |  | 
|  | /** | 
|  | * g_dbus_interface_info_cache_build: | 
|  | * @info: A #GDBusInterfaceInfo. | 
|  | * | 
|  | * Builds a lookup-cache to speed up | 
|  | * g_dbus_interface_info_lookup_method(), | 
|  | * g_dbus_interface_info_lookup_signal() and | 
|  | * g_dbus_interface_info_lookup_property(). | 
|  | * | 
|  | * If this has already been called with @info, the existing cache is | 
|  | * used and its use count is increased. | 
|  | * | 
|  | * Note that @info cannot be modified until | 
|  | * g_dbus_interface_info_cache_release() is called. | 
|  | * | 
|  | * Since: 2.30 | 
|  | */ | 
|  | void | 
|  | g_dbus_interface_info_cache_build (GDBusInterfaceInfo *info) | 
|  | { | 
|  | InfoCacheEntry *cache; | 
|  | guint n; | 
|  |  | 
|  | G_LOCK (info_cache_lock); | 
|  | if (info_cache == NULL) | 
|  | info_cache = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) info_cache_free); | 
|  | cache = g_hash_table_lookup (info_cache, info); | 
|  | if (cache != NULL) | 
|  | { | 
|  | cache->use_count += 1; | 
|  | goto out; | 
|  | } | 
|  | cache = g_slice_new0 (InfoCacheEntry); | 
|  | cache->use_count = 1; | 
|  | cache->method_name_to_data = g_hash_table_new (g_str_hash, g_str_equal); | 
|  | cache->signal_name_to_data = g_hash_table_new (g_str_hash, g_str_equal); | 
|  | cache->property_name_to_data = g_hash_table_new (g_str_hash, g_str_equal); | 
|  | for (n = 0; info->methods != NULL && info->methods[n] != NULL; n++) | 
|  | g_hash_table_insert (cache->method_name_to_data, info->methods[n]->name, info->methods[n]); | 
|  | for (n = 0; info->signals != NULL && info->signals[n] != NULL; n++) | 
|  | g_hash_table_insert (cache->signal_name_to_data, info->signals[n]->name, info->signals[n]); | 
|  | for (n = 0; info->properties != NULL && info->properties[n] != NULL; n++) | 
|  | g_hash_table_insert (cache->property_name_to_data, info->properties[n]->name, info->properties[n]); | 
|  | g_hash_table_insert (info_cache, info, cache); | 
|  | out: | 
|  | G_UNLOCK (info_cache_lock); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * g_dbus_interface_info_cache_release: | 
|  | * @info: A GDBusInterfaceInfo | 
|  | * | 
|  | * Decrements the usage count for the cache for @info built by | 
|  | * g_dbus_interface_info_cache_build() (if any) and frees the | 
|  | * resources used by the cache if the usage count drops to zero. | 
|  | * | 
|  | * Since: 2.30 | 
|  | */ | 
|  | void | 
|  | g_dbus_interface_info_cache_release (GDBusInterfaceInfo *info) | 
|  | { | 
|  | InfoCacheEntry *cache; | 
|  |  | 
|  | G_LOCK (info_cache_lock); | 
|  | if (G_UNLIKELY (info_cache == NULL)) | 
|  | { | 
|  | g_warning ("%s called for interface %s but there is no cache", info->name, G_STRFUNC); | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | cache = g_hash_table_lookup (info_cache, info); | 
|  | if (G_UNLIKELY (cache == NULL)) | 
|  | { | 
|  | g_warning ("%s called for interface %s but there is no cache entry", info->name, G_STRFUNC); | 
|  | goto out; | 
|  | } | 
|  | cache->use_count -= 1; | 
|  | if (cache->use_count == 0) | 
|  | { | 
|  | g_hash_table_remove (info_cache, info); | 
|  | /* could nuke info_cache itself if empty */ | 
|  | } | 
|  | out: | 
|  | G_UNLOCK (info_cache_lock); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* ---------------------------------------------------------------------------------------------------- */ | 
|  |  | 
|  | /** | 
|  | * g_dbus_node_info_lookup_interface: | 
|  | * @info: A #GDBusNodeInfo. | 
|  | * @name: A D-Bus interface name. | 
|  | * | 
|  | * Looks up information about an interface. | 
|  | * | 
|  | * The cost of this function is O(n) in number of interfaces. | 
|  | * | 
|  | * Returns: (nullable) (transfer none): A #GDBusInterfaceInfo or %NULL if not found. Do not free, it is owned by @info. | 
|  | * | 
|  | * Since: 2.26 | 
|  | */ | 
|  | GDBusInterfaceInfo * | 
|  | g_dbus_node_info_lookup_interface (GDBusNodeInfo *info, | 
|  | const gchar   *name) | 
|  | { | 
|  | guint n; | 
|  | GDBusInterfaceInfo *result; | 
|  |  | 
|  | for (n = 0; info->interfaces != NULL && info->interfaces[n] != NULL; n++) | 
|  | { | 
|  | GDBusInterfaceInfo *i = info->interfaces[n]; | 
|  |  | 
|  | if (g_strcmp0 (i->name, name) == 0) | 
|  | { | 
|  | result = i; | 
|  | goto out; | 
|  | } | 
|  | } | 
|  |  | 
|  | result = NULL; | 
|  |  | 
|  | out: | 
|  | return result; | 
|  | } |