/** * Creates a cluster-dict fetch specification. * * @param cluster_by A list of attributes to cluster by * @param cluster_data The fetch specifcation to use when filling the list * @return A cluster-list fetch specification */ xmmsv_t *xmmsv_build_cluster_dict (xmmsv_t *cluster_by, xmmsv_t *cluster_field, xmmsv_t *cluster_data) { xmmsv_t *res = xmmsv_new_dict (); if (res == NULL) return NULL; xmmsv_dict_set_string (res, "type", "cluster-dict"); if (cluster_by != NULL) { xmmsv_dict_set (res, "cluster-by", cluster_by); xmmsv_unref (cluster_by); } if (cluster_field != NULL) { xmmsv_dict_set (res, "cluster-field", cluster_field); xmmsv_unref (cluster_field); } if (cluster_data != NULL) { xmmsv_dict_set (res, "data", cluster_data); xmmsv_unref (cluster_data); } return res; }
static void filter_testcase (const gchar *path, xmmsv_t *list) { gchar *content, *filename; xmmsv_t *dict, *data, *coll; g_assert (g_file_get_contents (path, &content, NULL, NULL)); dict = xmmsv_from_json (content); if (dict == NULL) { g_error ("Could not parse '%s'!\n", path); g_assert_not_reached (); } g_free (content); g_assert (xmmsv_dict_has_key (dict, "medialib")); g_assert (xmmsv_dict_has_key (dict, "collection")); g_assert (xmmsv_dict_has_key (dict, "specification")); g_assert (xmmsv_dict_has_key (dict, "expected")); g_assert (xmmsv_dict_get (dict, "collection", &data)); g_assert (xmmsv_is_type (data, XMMSV_TYPE_DICT)); coll = xmmsv_coll_from_dict (data); xmmsv_dict_set (dict, "collection", coll); xmmsv_unref (coll); filename = g_path_get_basename (path); xmmsv_dict_set_string (dict, "name", filename); g_free (filename); xmmsv_list_append (list, dict); xmmsv_unref (dict); }
static xmmsv_t * convert_ghashtable_to_xmmsv (GHashTable *table, xmms_fetch_spec_t *spec) { GHashTableIter iter; s4_resultset_t *value; const gchar *key; xmmsv_t *ret; g_hash_table_iter_init (&iter, table); ret = xmmsv_new_dict (); while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &value)) { xmmsv_t *converted; if (value == NULL) { continue; } converted = xmms_medialib_query_to_xmmsv (value, spec); xmmsv_dict_set (ret, key, converted); xmmsv_unref (converted); } if (xmmsv_dict_get_size (ret) == 0) { xmmsv_unref (ret); ret = NULL; } return ret; }
static void currently_playing_update_info (currently_playing_t *entry, xmmsv_t *value) { const gchar *noinfo_fields[] = { "playback_status", "playtime", "position"}; const gchar *time_fields[] = { "duration"}; xmmsv_t *info; gint i; info = xmmsv_propdict_to_dict (value, NULL); enrich_mediainfo (info); /* copy over fields that are not from metadata */ for (i = 0; i < G_N_ELEMENTS (noinfo_fields); i++) { xmmsv_t *copy; if (xmmsv_dict_get (entry->data, noinfo_fields[i], ©)) { xmmsv_dict_set (info, noinfo_fields[i], copy); } } /* pretty format time fields */ for (i = 0; i < G_N_ELEMENTS (time_fields); i++) { gint32 tim; if (xmmsv_dict_entry_get_int (info, time_fields[i], &tim)) { gchar *p = format_time (tim, FALSE); xmmsv_dict_set_string (info, time_fields[i], p); g_free (p); } } xmmsv_unref (entry->data); entry->data = info; }
/** * Create a new argument for a method. * * @param name The name of the argument. Must not be NULL. * @param docstring The docstring of the argument. * @param type The expected type of the argument. Use XMMSV_TYPE_NONE to * accept any type. XMMSV_TYPE_ERROR is reserved and should not be used. * @param default_value Value to set this argument to if it's missing in the * method call. Implies that the argument is optional. If NULL, the argument * is not optional. */ xmmsv_t * xmmsv_sc_argument_new (const char *name, const char *docstring, xmmsv_type_t type, xmmsv_t *default_value) { xmmsv_t *arg; x_api_error_if (!name, "with NULL name.", NULL); x_api_error_if (type == XMMSV_TYPE_ERROR, "with ERROR type.", NULL); x_api_error_if (default_value && type != XMMSV_TYPE_NONE && xmmsv_get_type (default_value) != type, "with wrong type for default value.", NULL); arg = xmmsv_new_dict (); if (!arg) { x_oom (); return NULL; } xmmsv_dict_set_string (arg, "name", name); xmmsv_dict_set_int (arg, "type", type); if (docstring) { xmmsv_dict_set_string (arg, "docstring", docstring); } if (default_value) { xmmsv_dict_set (arg, "default_value", default_value); } return arg; }
xmmsv_t * xmmsv_build_dict_va (const char *firstkey, va_list ap) { const char *key; xmmsv_t *val, *res; res = xmmsv_new_dict (); if (!res) return NULL; key = firstkey; do { val = va_arg (ap, xmmsv_t *); if (!xmmsv_dict_set (res, key, val)) { xmmsv_unref (res); res = NULL; break; } xmmsv_unref (val); key = va_arg (ap, const char *); } while (key); return res; }
static gboolean create_xmmsv_dict_foreach (gpointer key, gpointer data, gpointer userdata) { const char *k = (const char *) key; xmmsv_t *v = (xmmsv_t *) data; xmmsv_t *l = (xmmsv_t *) userdata; xmmsv_dict_set (l, k, v); return FALSE; }
void xmms_xform_browse_add_entry_property (xmms_xform_t *xform, const gchar *key, xmmsv_t *val) { g_return_if_fail (xform); g_return_if_fail (xform->browse_dict); g_return_if_fail (key); g_return_if_fail (val); xmmsv_dict_set (xform->browse_dict, key, val); }
static void xmms_medialib_tree_add_tuple (xmmsv_t *dict, const char *key, const char *source, xmmsv_t *value) { xmmsv_t *entry; if (key == NULL || source == NULL || value == NULL) { return; } /* Find (or insert) subtree matching the prop key */ if (!xmmsv_dict_get (dict, key, &entry)) { entry = xmmsv_new_dict (); xmmsv_dict_set (dict, key, entry); xmmsv_unref (entry); } /* Replace (or insert) value matching the prop source */ xmmsv_dict_set (entry, source, value); }
/* Converts an S4 resultset into an xmmsv_t, based on the fetch specification */ xmmsv_t * xmms_medialib_query_to_xmmsv (s4_resultset_t *set, xmms_fetch_spec_t *spec) { GHashTable *set_table; GList *sets; xmmsv_t *val, *ret = NULL; gint i; switch (spec->type) { case FETCH_COUNT: ret = xmmsv_new_int (s4_resultset_get_rowcount (set)); break; case FETCH_METADATA: ret = metadata_to_xmmsv (set, spec); break; case FETCH_ORGANIZE: ret = xmmsv_new_dict (); for (i = 0; i < spec->data.organize.count; i++) { val = xmms_medialib_query_to_xmmsv (set, spec->data.organize.data[i]); if (val != NULL) { xmmsv_dict_set (ret, spec->data.organize.keys[i], val); xmmsv_unref (val); } } break; case FETCH_CLUSTER_LIST: sets = cluster_list (set, spec); ret = xmmsv_new_list (); for (; sets != NULL; sets = g_list_delete_link (sets, sets)) { set = sets->data; val = xmms_medialib_query_to_xmmsv (set, spec->data.cluster.data); if (val != NULL) { xmmsv_list_append (ret, val); xmmsv_unref (val); } s4_resultset_free (set); } break; case FETCH_CLUSTER_DICT: set_table = cluster_dict (set, spec); ret = convert_ghashtable_to_xmmsv (set_table, spec->data.cluster.data); g_hash_table_destroy (set_table); break; default: g_assert_not_reached (); } return ret; }
/** * Creates a metadata fetch specification. * * @param fields A list of fields to fetch, or NULL to fetch everything * @param get A list of what to get ("id", "key", "value", "source") * @param aggregate The aggregation function to use * @param sourcepref A list of sources, first one has the highest priority * @return A metadata fetch specification */ xmmsv_t *xmmsv_build_metadata (xmmsv_t *fields, xmmsv_t *get, const char *aggregate, xmmsv_t *sourcepref) { xmmsv_t *res = xmmsv_new_dict (); if (res == NULL) return NULL; xmmsv_dict_set_string (res, "type", "metadata"); if (fields != NULL) { if (xmmsv_get_type (fields) == XMMSV_TYPE_STRING) { xmmsv_t *list = xmmsv_new_list (); xmmsv_list_append (list, fields); xmmsv_unref (fields); fields = list; } xmmsv_dict_set (res, "fields", fields); xmmsv_unref (fields); } if (get != NULL) { if (xmmsv_get_type (get) == XMMSV_TYPE_STRING) { xmmsv_t *list = xmmsv_new_list (); xmmsv_list_append (list, get); xmmsv_unref (get); get = list; } xmmsv_dict_set (res, "get", get); xmmsv_unref (get); } if (sourcepref != NULL) { xmmsv_dict_set (res, "source-preference", sourcepref); xmmsv_unref (sourcepref); } if (aggregate != NULL) { xmmsv_dict_set_string (res, "aggregate", aggregate); } return res; }
static int append (xmmsv_t *obj, const char *key, uint32_t key_len, xmmsv_t *value) { if (xmmsv_is_type (obj, XMMSV_TYPE_LIST)) { xmmsv_list_append (obj, value); } else if (xmmsv_is_type (obj, XMMSV_TYPE_DICT) && key) { xmmsv_dict_set (obj, key, value); } else { /* Should never be reached */ assert (0); } xmmsv_unref (value); return 0; }
/** * Creates an organize fetch specification that may be passed to xmmsc_coll_query. * It takes a dict with key-value pairs where the values are fetch specifications. * * @return An organize fetch specification */ xmmsv_t * xmmsv_build_organize (xmmsv_t *data) { xmmsv_t *res; x_return_val_if_fail (data != NULL, NULL); res = xmmsv_new_dict (); if (res != NULL) { xmmsv_dict_set_string (res, "type", "organize"); xmmsv_dict_set (res, "data", data); xmmsv_unref (data); } return res; }
/** * Helper function to transform a key-source-value dict-of-dict * #xmmsv_t (formerly a propdict) to a regular key-value dict, given a * list of source preference. * * @param propdict A key-source-value dict-of-dict #xmmsv_t. * @param src_prefs A list of source names or patterns. Must be * NULL-terminated. If this argument is NULL, the * default source preferences is used. * @return An #xmmsv_t containing a simple key-value dict. Must be * unreffed manually when done. */ xmmsv_t * xmmsv_propdict_to_dict (xmmsv_t *propdict, const char **src_prefs) { xmmsv_t *dict, *source_dict, *value, *best_value; xmmsv_dict_iter_t *key_it, *source_it; const char *key, *source; const char **local_prefs; int match_index, best_index; dict = xmmsv_new_dict (); local_prefs = src_prefs ? src_prefs : xmmsv_default_source_pref; xmmsv_get_dict_iter (propdict, &key_it); while (xmmsv_dict_iter_valid (key_it)) { xmmsv_dict_iter_pair (key_it, &key, &source_dict); best_value = NULL; best_index = -1; xmmsv_get_dict_iter (source_dict, &source_it); while (xmmsv_dict_iter_valid (source_it)) { xmmsv_dict_iter_pair (source_it, &source, &value); match_index = find_match_index (source, local_prefs); /* keep first match or better match */ if (match_index >= 0 && (best_index < 0 || match_index < best_index)) { best_value = value; best_index = match_index; } xmmsv_dict_iter_next (source_it); } /* Note: we do not insert a key-value pair if no source matches */ if (best_value) { xmmsv_dict_set (dict, key, best_value); } xmmsv_dict_iter_next (key_it); } return dict; }
static int xmmsc_deserialize_dict (xmmsv_t *bb, xmmsv_t **val) { xmmsv_t *dict; int32_t len; unsigned int ignore; char *key; dict = xmmsv_new_dict (); if (!_internal_get_from_bb_int32_positive (bb, &len)) { goto err; } while (len--) { xmmsv_t *v; if (!_internal_get_from_bb_string_alloc (bb, &key, &ignore)) { goto err; } if (!xmmsv_bitbuffer_deserialize_value (bb, &v)) { free (key); goto err; } xmmsv_dict_set (dict, key, v); free (key); xmmsv_unref (v); } *val = dict; return true; err: x_internal_error ("Message from server did not parse correctly!"); xmmsv_unref (dict); return false; }
xmmsv_t * duplicate_dict_value (xmmsv_t *val) { xmmsv_t *dup_val; xmmsv_dict_iter_t *it; const char *key; xmmsv_t *v; xmmsv_t *new_elem; x_return_val_if_fail (xmmsv_get_dict_iter (val, &it), NULL); dup_val = xmmsv_new_dict (); while (xmmsv_dict_iter_pair (it, &key, &v)) { new_elem = xmmsv_copy (v); xmmsv_dict_set (dup_val, key, new_elem); xmmsv_unref (new_elem); xmmsv_dict_iter_next (it); } xmmsv_dict_iter_explicit_destroy (it); return dup_val; }
static xmmsv_t * args_to_dict (gchar **argv, gint argc) { xmmsv_t *dict; gint i; dict = xmmsv_new_dict (); if (!dict) { return NULL; } for (i = 0; i < argc; i++) { gchar **tuple; xmmsv_t *val; /* Split the string into arg and value. */ tuple = g_strsplit (argv[i], "=", 2); if (tuple[0] && tuple[1]) { val = xmmsv_new_string (tuple[1]); } else if (tuple[0]) { val = xmmsv_new_none (); } else { g_strfreev (tuple); continue; /* Empty tuple, means empty string. */ } if (!val) { xmmsv_unref (dict); return NULL; } xmmsv_dict_set (dict, tuple[0], val); g_strfreev (tuple); xmmsv_unref (val); } return dict; }
/** * Decodes a cluster fetch specification from a dictionary. * The 'cluster-by' must be one of 'id', 'position' or 'value'. If set * to 'value', then an additional 'cluster-field' will be used to specify * which meta data attribute to cluster on. */ static xmms_fetch_spec_t * xmms_fetch_spec_new_cluster (xmmsv_t *fetch, xmms_fetch_info_t *info, s4_sourcepref_t *prefs, xmms_error_t *err) { xmmsv_t *cluster_by, *cluster_field, *cluster_data; xmms_fetch_spec_t *data, *spec = NULL; s4_sourcepref_t *sp; const gchar *value = NULL; const gchar *field = NULL; const gchar *fallback = NULL; gint cluster_type; if (!xmmsv_dict_get (fetch, "cluster-by", &cluster_by)) { cluster_by = xmmsv_new_string ("value"); xmmsv_dict_set (fetch, "cluster-by", cluster_by); xmmsv_unref (cluster_by); } if (!xmmsv_dict_entry_get_string (fetch, "cluster-by", &value)) { const gchar *message = "'cluster-by' must be a string."; xmms_error_set (err, XMMS_ERROR_INVAL, message); return NULL; } xmmsv_get_string (cluster_by, &value); if (!cluster_by_from_string (value, &cluster_type)) { const gchar *message = "'cluster-by' must be 'id', 'position', or 'value'."; xmms_error_set (err, XMMS_ERROR_INVAL, message); return NULL; } if (cluster_type == CLUSTER_BY_VALUE) { if (!xmmsv_dict_entry_get_string (fetch, "cluster-field", &field)) { const gchar *message = "'cluster-field' must if 'cluster-by' is 'value'."; xmms_error_set (err, XMMS_ERROR_INVAL, message); return NULL; } } if (!xmmsv_dict_get (fetch, "data", &cluster_data)) { const gchar *message = "Required field 'data' not set in cluster."; xmms_error_set (err, XMMS_ERROR_INVAL, message); return NULL; } if (xmmsv_dict_entry_get_type (fetch, "cluster-fallback") == XMMSV_TYPE_NONE) { fallback = NULL; } else if (!xmmsv_dict_entry_get_string (fetch, "cluster-fallback", &fallback)) { const gchar *message = "Optional field 'default' must be a string."; xmms_error_set (err, XMMS_ERROR_INVAL, message); return NULL; } sp = normalize_source_preferences (fetch, prefs, err); if (xmms_error_iserror (err)) { return NULL; } data = xmms_fetch_spec_new (cluster_data, info, sp, err); if (xmms_error_iserror (err)) { s4_sourcepref_unref (sp); return NULL; } spec = g_new0 (xmms_fetch_spec_t, 1); spec->data.cluster.data = data; spec->data.cluster.type = cluster_type; spec->data.cluster.fallback = fallback; switch (spec->data.cluster.type) { case CLUSTER_BY_ID: spec->data.cluster.column = xmms_fetch_info_add_song_id(info, cluster_field); break; case CLUSTER_BY_VALUE: xmmsv_dict_get (fetch, "cluster-field", &cluster_field); spec->data.cluster.column = xmms_fetch_info_add_key (info, cluster_field, field, sp); break; case CLUSTER_BY_POSITION: /* do nothing */ break; default: g_assert_not_reached (); } s4_sourcepref_unref (sp); return spec; }
static bool _internal_get_from_bb_collection_alloc (xmmsv_t *bb, xmmsv_coll_t **coll) { int i; int32_t type; int32_t n_items; int id; int32_t *idlist = NULL; xmmsv_t *dict, *attrs; xmmsv_dict_iter_t *it; /* Get the type and create the collection */ if (!_internal_get_from_bb_int32_positive (bb, &type)) { return false; } *coll = xmmsv_coll_new (type); /* Get the attributes */ if (!_internal_get_from_bb_value_dict_alloc (bb, &dict)) { return false; } attrs = xmmsv_coll_attributes_get (*coll); xmmsv_get_dict_iter (dict, &it); while (xmmsv_dict_iter_valid (it)) { const char *key; xmmsv_t *value; xmmsv_dict_iter_pair (it, &key, &value); xmmsv_dict_set (attrs, key, value); xmmsv_dict_iter_next (it); } xmmsv_unref (dict); /* Get the idlist */ if (!_internal_get_from_bb_int32_positive (bb, &n_items)) { goto err; } if (!(idlist = x_new (int32_t, n_items + 1))) { goto err; } for (i = 0; i < n_items; i++) { if (!_internal_get_from_bb_int32 (bb, &id)) { goto err; } idlist[i] = id; } idlist[i] = 0; xmmsv_coll_set_idlist (*coll, idlist); free (idlist); idlist = NULL; /* Get the operands */ if (!_internal_get_from_bb_int32_positive (bb, &n_items)) { goto err; } for (i = 0; i < n_items; i++) { xmmsv_coll_t *operand; if (!_internal_get_from_bb_int32_positive (bb, &type) || type != XMMSV_TYPE_COLL || !_internal_get_from_bb_collection_alloc (bb, &operand)) { goto err; } xmmsv_coll_add_operand (*coll, operand); xmmsv_coll_unref (operand); } return true; err: if (idlist != NULL) { free (idlist); } xmmsv_coll_unref (*coll); return false; }
/** Sorts the playlist by properties. * * This will sort the list. * @param playlist The playlist to sort. * @param properties Tells xmms_playlist_sort which properties it * should use when sorting. * @param err An #xmms_error_t - needed since xmms_playlist_sort is an ipc * method handler. */ static void xmms_playlist_client_sort (xmms_playlist_t *playlist, const gchar *plname, xmmsv_t *properties, xmms_error_t *err) { xmmsv_t *tmp, *idlist, *val, *spec, *metadata, *get; xmmsv_coll_t *plcoll, *ordered; gint currpos, pos; xmms_medialib_entry_t currid; g_return_if_fail (playlist); g_return_if_fail (properties); if (xmmsv_list_get_size (properties) < 1) { xmms_error_set (err, XMMS_ERROR_NOENT, "need at least one property to sort"); return; } g_mutex_lock (playlist->mutex); plcoll = xmms_playlist_get_coll (playlist, plname, err); if (plcoll == NULL) { xmms_error_set (err, XMMS_ERROR_NOENT, "no such playlist!"); g_mutex_unlock (playlist->mutex); return; } currpos = xmms_playlist_coll_get_currpos (plcoll); xmmsv_coll_idlist_get_index (plcoll, currpos, &currid); get = xmmsv_new_list (); xmmsv_list_append_string (get, "id"); metadata = xmmsv_new_dict (); xmmsv_dict_set_string (metadata, "type", "metadata"); xmmsv_dict_set_string (metadata, "aggregate", "first"); xmmsv_dict_set (metadata, "get", get); xmmsv_unref (get); spec = xmmsv_new_dict (); xmmsv_dict_set_string (spec, "type", "cluster-list"); xmmsv_dict_set_string (spec, "cluster-by", "position"); xmmsv_dict_set (spec, "data", metadata); xmmsv_unref (metadata); ordered = xmmsv_coll_add_order_operators (plcoll, properties); MEDIALIB_BEGIN (playlist->medialib); tmp = xmms_medialib_query (session, ordered, spec, err); MEDIALIB_COMMIT (); xmmsv_coll_unref (ordered); xmmsv_unref (spec); if (tmp == NULL) { g_mutex_unlock (playlist->mutex); return; } idlist = xmmsv_coll_idlist_get (plcoll); xmmsv_list_clear (idlist); for (pos = 0; xmmsv_list_get (tmp, pos, &val); pos++) { xmms_medialib_entry_t id; xmmsv_get_int (val, &id); xmmsv_list_append (idlist, val); if (id == currid) { xmms_collection_set_int_attr (plcoll, "position", pos); currpos = pos; } } xmmsv_unref (tmp); XMMS_PLAYLIST_CHANGED_MSG (XMMS_PLAYLIST_CHANGED_SORT, 0, plname); XMMS_PLAYLIST_CURRPOS_MSG (currpos, plname); g_mutex_unlock (playlist->mutex); }
/* Converts an S4 result (a column) into an xmmsv values */ static void * result_to_xmmsv (xmmsv_t *ret, gint32 id, const s4_result_t *res, xmms_fetch_spec_t *spec) { static xmmsv_t * (*aggregate_functions[AGGREGATE_END])(xmmsv_t *c, gint i, const gchar *s) = { aggregate_first, aggregate_sum, aggregate_max, aggregate_min, aggregate_set, aggregate_list, aggregate_random, aggregate_average }; const s4_val_t *val; xmmsv_t *dict, *current; const gchar *str_value, *key = NULL; gint32 i, int_value; xmmsv_t *newval; g_return_val_if_fail (spec->data.metadata.get_size > 0, ret); g_return_val_if_fail (spec->data.metadata.get_size <= METADATA_END, ret); g_return_val_if_fail (spec->data.metadata.aggr_func >= 0, ret); g_return_val_if_fail (spec->data.metadata.aggr_func < AGGREGATE_END, ret); /* Loop through all the values the column has */ while (res != NULL) { dict = ret; current = ret; /* Loop through the list of what to get ("key", "source", ..) */ for (i = 0; i < spec->data.metadata.get_size; i++) { str_value = NULL; int_value = 0; /* Fill str_value with the correct value if it is a string * or int_value if it is an integer */ switch (spec->data.metadata.get[i]) { case METADATA_KEY: str_value = s4_result_get_key (res); break; case METADATA_SOURCE: str_value = s4_result_get_src (res); if (str_value == NULL) str_value = "server"; break; case METADATA_ID: int_value = id; break; case METADATA_VALUE: val = s4_result_get_val (res); if (!s4_val_get_int (val, &int_value)) { s4_val_get_str (val, &str_value); } break; default: g_assert_not_reached (); } /* If this is not the last property to get we use this property * as a key in a dict */ if (i < (spec->data.metadata.get_size - 1)) { /* Convert integers to strings */ if (str_value == NULL) { /* Big enough to hold 2^32 with minus sign */ gchar buf[12]; g_sprintf (buf, "%i", int_value); key = buf; } else { key = str_value; } /* Make sure the root dict exists */ if (dict == NULL) { ret = dict = xmmsv_new_dict (); } /* If this dict contains dicts we have to create a new * dict if one does not exists for the key yet */ if (!xmmsv_dict_get (dict, key, ¤t)) current = NULL; if (i < (spec->data.metadata.get_size - 2)) { if (current == NULL) { current = xmmsv_new_dict (); xmmsv_dict_set (dict, key, current); xmmsv_unref (current); } dict = current; } } } newval = aggregate_functions[spec->data.metadata.aggr_func](current, int_value, str_value); /* Update the previous dict (if there is one) */ if (newval != current) { if (i > 1) { xmmsv_dict_set (dict, key, newval); xmmsv_unref (newval); } else { ret = newval; if (current != NULL) { xmmsv_unref (current); } } } res = s4_result_next (res); } return ret; }
/** * Set an attribute in the given collection. * * @param coll The collection in which to set the attribute. * @param key The name of the attribute to set. * @param value The value of the attribute. */ void xmmsv_coll_attribute_set_value (xmmsv_t *coll, const char *key, xmmsv_t *value) { x_return_if_fail (xmmsv_is_type (coll, XMMSV_TYPE_COLL)); xmmsv_dict_set (coll->value.coll->attributes, key, value); }