/* GVariant to dbus-glib escape hatch
*
* Copyright © 2010 Collabora Ltd.
*
* Licensed under the Academic Free License version 2.1
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Alternatively, at your option, you can redistribute and/or modify
* this single file under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* that license, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include
#include
#include
#include
/* Static functions in this file are a bit weird: they take a GVariant as first
* argument, but it can be NULL. If it is, @value will be initialized with the
* right type for that GVariant, but not filled in, so it'll contain 0 or NULL
* or whatever. */
static void dbus_g_value_parse_variant_by_type (GVariant *variant,
const GVariantType *variant_type,
GValue *value);
static void
dbus_g_value_dict_parse_variant (GVariant *variant,
const GVariantType *member_type,
GValue *value)
{
const GVariantType *key_type, *value_type;
GValue key_parsed = { 0 }, value_parsed = { 0 };
g_assert (g_variant_type_is_dict_entry (member_type));
key_type = g_variant_type_key (member_type);
value_type = g_variant_type_value (member_type);
/* first get the GTypes, without getting actual values */
dbus_g_value_parse_variant_by_type (NULL, key_type, &key_parsed);
dbus_g_value_parse_variant_by_type (NULL, value_type, &value_parsed);
g_value_init (value, dbus_g_type_get_map ("GHashTable",
G_VALUE_TYPE (&key_parsed),
G_VALUE_TYPE (&value_parsed)));
g_value_unset (&key_parsed);
g_value_unset (&value_parsed);
if (variant != NULL)
{
GVariantIter iter;
GVariant *child;
DBusGTypeSpecializedAppendContext ctx;
g_value_take_boxed (value, dbus_g_type_specialized_construct (
G_VALUE_TYPE (value)));
dbus_g_type_specialized_init_append (value, &ctx);
g_variant_iter_init (&iter, variant);
for (child = g_variant_iter_next_value (&iter);
child != NULL;
child = g_variant_iter_next_value (&iter))
{
GVariant *grandchild;
grandchild = g_variant_get_child_value (child, 0);
dbus_g_value_parse_variant_by_type (grandchild, key_type,
&key_parsed);
g_variant_unref (grandchild);
grandchild = g_variant_get_child_value (child, 1);
dbus_g_value_parse_variant_by_type (grandchild, value_type,
&value_parsed);
g_variant_unref (grandchild);
/* Here be dragons: this steals the *contents of* key_parsed and
* value_parsed, so we can't g_value_unset() them. */
dbus_g_type_specialized_map_append (&ctx, &key_parsed,
&value_parsed);
memset (&key_parsed, '\0', sizeof (key_parsed));
memset (&value_parsed, '\0', sizeof (value_parsed));
g_variant_unref (child);
}
}
}
static void
dbus_g_value_basic_array_parse_variant (GVariant *variant,
gchar type_char,
GValue *value)
{
GType gtype = G_TYPE_INVALID;
guint dg_size = 0, gv_size = 0;
switch ((GVariantClass) type_char)
{
case G_VARIANT_CLASS_STRING:
g_value_init (value, G_TYPE_STRV);
if (variant != NULL)
g_value_take_boxed (value, g_variant_dup_strv (variant, NULL));
return;
case G_VARIANT_CLASS_OBJECT_PATH:
case G_VARIANT_CLASS_SIGNATURE:
{
if (type_char == G_VARIANT_CLASS_OBJECT_PATH)
gtype = DBUS_TYPE_G_OBJECT_PATH;
else
gtype = DBUS_TYPE_G_SIGNATURE;
g_value_init (value,
dbus_g_type_get_collection ("GPtrArray", gtype));
if (variant != NULL)
{
gsize n = g_variant_n_children (variant);
gsize i;
GPtrArray *pa = g_ptr_array_sized_new (n);
for (i = 0; i < n; i++)
{
GVariant *child = g_variant_get_child_value (variant, i);
gchar *s = g_variant_dup_string (child, NULL);
g_ptr_array_add (pa, s);
g_variant_unref (child);
}
g_value_take_boxed (value, pa);
}
}
return;
/* From here down handles fixed-size types. */
case G_VARIANT_CLASS_BYTE:
gtype = G_TYPE_UCHAR;
gv_size = dg_size = sizeof (guchar);
break;
case G_VARIANT_CLASS_BOOLEAN:
gtype = G_TYPE_BOOLEAN;
dg_size = sizeof (gboolean);
gv_size = sizeof (guchar);
break;
case G_VARIANT_CLASS_INT16:
gtype = G_TYPE_INT;
dg_size = sizeof (gint);
gv_size = sizeof (gint16);
break;
case G_VARIANT_CLASS_INT32:
gtype = G_TYPE_INT;
dg_size = sizeof (gint);
gv_size = sizeof (gint32);
break;
case G_VARIANT_CLASS_UINT16:
gtype = G_TYPE_UINT;
dg_size = sizeof (guint);
gv_size = sizeof (guint16);
break;
case G_VARIANT_CLASS_UINT32:
gtype = G_TYPE_UINT;
dg_size = sizeof (guint);
gv_size = sizeof (guint32);
break;
case G_VARIANT_CLASS_INT64:
gtype = G_TYPE_INT64;
dg_size = gv_size = sizeof (gint64);
break;
case G_VARIANT_CLASS_UINT64:
gtype = G_TYPE_UINT64;
dg_size = gv_size = sizeof (guint64);
break;
case G_VARIANT_CLASS_DOUBLE:
gtype = G_TYPE_DOUBLE;
dg_size = gv_size = sizeof (gdouble);
break;
case G_VARIANT_CLASS_HANDLE:
case G_VARIANT_CLASS_VARIANT:
case G_VARIANT_CLASS_MAYBE:
case G_VARIANT_CLASS_ARRAY:
case G_VARIANT_CLASS_TUPLE:
case G_VARIANT_CLASS_DICT_ENTRY:
g_return_if_reached ();
}
g_assert (gtype != G_TYPE_INVALID);
g_assert (dg_size != 0);
g_assert (gv_size != 0);
g_value_init (value, dbus_g_type_get_collection ("GArray", gtype));
if (variant != NULL)
{
GArray *arr;
gsize n, i;
gconstpointer blob = g_variant_get_fixed_array (variant, &n, gv_size);
arr = g_array_sized_new (FALSE, FALSE, dg_size, n);
g_value_take_boxed (value, arr);
if (dg_size == gv_size)
{
/* fast path: we can just memcpy them in */
g_array_append_vals (arr, blob, n);
}
else
{
DBusGTypeSpecializedAppendContext ctx;
dbus_g_type_specialized_init_append (value, &ctx);
for (i = 0; i < n; i++)
{
GVariant *child;
GValue v = { 0 };
child = g_variant_get_child_value (variant, i);
dbus_g_value_parse_g_variant (child, &v);
g_variant_unref (child);
dbus_g_type_specialized_collection_append (&ctx, &v);
}
dbus_g_type_specialized_collection_end_append (&ctx);
}
}
}
static void
dbus_g_value_tuple_parse_variant (GVariant *variant,
const GVariantType *variant_type,
GValue *value)
{
gsize n = g_variant_type_n_items (variant_type);
GType *types;
gsize i;
GValueArray *va = g_value_array_new (n);
const GVariantType *inner_type;
types = g_new0 (GType, n);
for (i = 0, inner_type = g_variant_type_first (variant_type);
i < n;
i++, inner_type = g_variant_type_next (inner_type))
{
GVariant *inner_variant;
if (variant == NULL)
inner_variant = NULL;
else
inner_variant = g_variant_get_child_value (variant, i);
g_value_array_append (va, NULL);
dbus_g_value_parse_variant_by_type (inner_variant, inner_type,
&va->values[i]);
types[i] = G_VALUE_TYPE (&va->values[i]);
if (inner_variant != NULL)
g_variant_unref (inner_variant);
}
g_value_init (value, dbus_g_type_get_structv ("GValueArray", n, types));
if (variant == NULL)
g_value_array_free (va);
else
g_value_take_boxed (value, va);
g_free (types);
}
static void
dbus_g_value_array_parse_variant (GVariant *variant,
const GVariantType *variant_type,
GValue *value)
{
const GVariantType *member_type;
gchar type_char;
GPtrArray *pa = NULL;
gsize n = 0, i;
g_assert (g_variant_type_is_array (variant_type));
member_type = g_variant_type_element (variant_type);
type_char = g_variant_type_peek_string (member_type)[0];
if (g_variant_type_is_dict_entry (member_type))
{
dbus_g_value_dict_parse_variant (variant, member_type, value);
return;
}
if (g_variant_type_is_basic (member_type))
{
dbus_g_value_basic_array_parse_variant (variant, type_char, value);
return;
}
/* all the non-basic types end up as a GPtrArray of boxed */
if (variant != NULL)
{
n = g_variant_n_children (variant);
pa = g_ptr_array_sized_new (n);
}
switch ((GVariantClass) type_char)
{
case G_VARIANT_CLASS_VARIANT:
{
g_value_init (value,
dbus_g_type_get_collection ("GPtrArray", G_TYPE_VALUE));
}
break;
case G_VARIANT_CLASS_ARRAY:
{
GValue v = { 0 };
dbus_g_value_array_parse_variant (NULL, member_type, &v);
g_value_init (value, dbus_g_type_get_collection ("GPtrArray",
G_VALUE_TYPE (&v)));
}
break;
case G_VARIANT_CLASS_TUPLE:
{
GValue v = { 0 };
dbus_g_value_tuple_parse_variant (NULL, member_type, &v);
g_value_init (value, dbus_g_type_get_collection ("GPtrArray",
G_VALUE_TYPE (&v)));
}
break;
case G_VARIANT_CLASS_DICT_ENTRY:
case G_VARIANT_CLASS_MAYBE:
default:
g_critical ("unhandled GVariantClass array<%d>", type_char);
g_return_if_reached ();
}
if (variant != NULL)
{
for (i = 0; i < n; i++)
{
GVariant *child = g_variant_get_child_value (variant, i);
GValue tmp = { 0 };
dbus_g_value_parse_g_variant (child, &tmp);
g_ptr_array_add (pa, g_value_dup_boxed (&tmp));
g_variant_unref (child);
g_value_unset (&tmp);
}
g_value_take_boxed (value, pa);
}
}
static void
dbus_g_value_parse_variant_by_type (GVariant *variant,
const GVariantType *variant_type,
GValue *value)
{
gchar type_char = g_variant_type_peek_string (variant_type)[0];
switch ((GVariantClass) type_char)
{
case G_VARIANT_CLASS_BOOLEAN:
g_value_init (value, G_TYPE_BOOLEAN);
if (variant != NULL)
g_value_set_boolean (value, !!g_variant_get_boolean (variant));
break;
case G_VARIANT_CLASS_BYTE:
g_value_init (value, G_TYPE_UCHAR);
if (variant != NULL)
g_value_set_uchar (value, g_variant_get_byte (variant));
break;
case G_VARIANT_CLASS_UINT16:
/* there is no G_TYPE_UINT16 */
g_value_init (value, G_TYPE_UINT);
if (variant != NULL)
g_value_set_uint (value, g_variant_get_uint16 (variant));
break;
case G_VARIANT_CLASS_UINT32:
g_value_init (value, G_TYPE_UINT);
if (variant != NULL)
g_value_set_uint (value, g_variant_get_uint32 (variant));
break;
case G_VARIANT_CLASS_UINT64:
g_value_init (value, G_TYPE_UINT64);
if (variant != NULL)
g_value_set_uint64 (value, g_variant_get_uint64 (variant));
break;
case G_VARIANT_CLASS_INT16:
/* there is no G_TYPE_INT16 */
g_value_init (value, G_TYPE_INT);
if (variant != NULL)
g_value_set_int (value, g_variant_get_int16 (variant));
break;
case G_VARIANT_CLASS_INT32:
g_value_init (value, G_TYPE_INT);
if (variant != NULL)
g_value_set_int (value, g_variant_get_int32 (variant));
break;
case G_VARIANT_CLASS_INT64:
g_value_init (value, G_TYPE_INT64);
if (variant != NULL)
g_value_set_int64 (value, g_variant_get_int64 (variant));
break;
case G_VARIANT_CLASS_DOUBLE:
g_value_init (value, G_TYPE_DOUBLE);
if (variant != NULL)
g_value_set_double (value, g_variant_get_double (variant));
break;
case G_VARIANT_CLASS_STRING:
g_value_init (value, G_TYPE_STRING);
if (variant != NULL)
g_value_set_string (value, g_variant_get_string (variant, NULL));
break;
case G_VARIANT_CLASS_OBJECT_PATH:
g_value_init (value, DBUS_TYPE_G_OBJECT_PATH);
if (variant != NULL)
g_value_set_boxed (value, g_variant_get_string (variant, NULL));
break;
case G_VARIANT_CLASS_SIGNATURE:
g_value_init (value, DBUS_TYPE_G_SIGNATURE);
if (variant != NULL)
g_value_set_boxed (value, g_variant_get_string (variant, NULL));
break;
case G_VARIANT_CLASS_VARIANT:
g_value_init (value, G_TYPE_VALUE);
if (variant != NULL)
{
GVariant *inner_variant = g_variant_get_variant (variant);
GValue *inner_value = g_new0 (GValue, 1);
dbus_g_value_parse_g_variant (inner_variant, inner_value);
g_value_take_boxed (value, inner_value);
g_variant_unref (inner_variant);
}
break;
case G_VARIANT_CLASS_ARRAY:
dbus_g_value_array_parse_variant (variant, variant_type, value);
break;
case G_VARIANT_CLASS_TUPLE:
dbus_g_value_tuple_parse_variant (variant, variant_type, value);
break;
case G_VARIANT_CLASS_DICT_ENTRY:
g_critical ("found a dict entry not in a dict");
break;
case G_VARIANT_CLASS_HANDLE:
case G_VARIANT_CLASS_MAYBE:
g_critical ("unhandled GVariantClass '%c' (%d)",
CLAMP (type_char, ' ', '~'),
type_char);
break;
}
}
/**
* dbus_g_value_parse_g_variant:
* @variant: a #GVariant
* @value: a zero-filled #GValue
*
* Deserialize @variant and put an equivalent dbus-glib data structure in
* @value.
*
* It is an error if @variant contains any #GVariant extensions not supported
* by dbus-glib, including handles (file descriptor passing) and 'maybe' types.
*
* Deprecated: New code should use GDBus instead.
*/
void
dbus_g_value_parse_g_variant (GVariant *variant,
GValue *value)
{
g_return_if_fail (variant != NULL);
dbus_g_value_parse_variant_by_type (variant, g_variant_get_type (variant),
value);
}