XMMS2
collection.c
Go to the documentation of this file.
1 /* XMMS2 - X Music Multiplexer System
2  * Copyright (C) 2003-2009 XMMS2 Team
3  *
4  * PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!!
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  */
16 
17 
18 /** @file
19  * Manages collections
20  */
21 
22 #include <stdio.h>
23 #include <unistd.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <glib.h>
27 #include <math.h>
28 
30 #include "xmmspriv/xmms_playlist.h"
33 #include "xmmspriv/xmms_collsync.h"
34 #include "xmmspriv/xmms_xform.h"
36 #include "xmms/xmms_ipc.h"
37 #include "xmms/xmms_config.h"
38 #include "xmms/xmms_log.h"
39 
40 
41 /* Internal helper structures */
42 
43 typedef struct {
44  const gchar *name;
45  const gchar *namespace;
46  xmmsv_coll_t *oldtarget;
47  xmmsv_coll_t *newtarget;
48 } coll_rebind_infos_t;
49 
50 typedef struct {
51  const gchar* oldname;
52  const gchar* newname;
53  const gchar* namespace;
54 } coll_rename_infos_t;
55 
56 typedef struct {
57  xmms_coll_dag_t *dag;
58  FuncApplyToColl func;
59  void *udata;
60 } coll_call_infos_t;
61 
62 typedef struct {
63  const gchar *target_name;
64  const gchar *target_namespace;
65  gboolean found;
66 } coll_refcheck_t;
67 
68 typedef struct {
69  const gchar *key;
70  xmmsv_coll_t *value;
71 } coll_table_pair_t;
72 
73 typedef enum {
78 
79 typedef struct add_metadata_from_tree_user_data_St {
80  xmms_medialib_session_t *session;
82  guint src;
84 
85 static GList *global_stream_type;
86 
87 /* Functions */
88 
89 static void xmms_collection_destroy (xmms_object_t *object);
90 
91 static gboolean xmms_collection_validate (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, const gchar *save_name, const gchar *save_namespace);
92 static gboolean xmms_collection_validate_recurs (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, const gchar *save_name, const gchar *save_namespace);
93 static gboolean xmms_collection_unreference (xmms_coll_dag_t *dag, const gchar *name, guint nsid);
94 
95 static gboolean xmms_collection_has_reference_to (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, const gchar *tg_name, const gchar *tg_ns);
96 
97 static void xmms_collection_apply_to_collection_recurs (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, FuncApplyToColl f, void *udata);
98 
99 static void call_apply_to_coll (gpointer name, gpointer coll, gpointer udata);
100 static void prepend_key_string (gpointer key, gpointer value, gpointer udata);
101 static gboolean value_match_save_key (gpointer key, gpointer val, gpointer udata);
102 
103 static void rebind_references (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata);
104 static void rename_references (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata);
105 static void strip_references (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata);
106 static void check_for_reference (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata);
107 
108 static void coll_unref (void *coll);
109 
110 static GHashTable *xmms_collection_media_info (guint mid, xmms_error_t *err);
111 
112 static gboolean filter_get_mediainfo_field_string (xmmsv_coll_t *coll, GHashTable *mediainfo, gchar **val);
113 static gboolean filter_get_mediainfo_field_int (xmmsv_coll_t *coll, GHashTable *mediainfo, gint *val);
114 static gboolean filter_get_operator_value_string (xmmsv_coll_t *coll, const gchar **val);
115 static gboolean filter_get_operator_value_int (xmmsv_coll_t *coll, gint *val);
116 static gboolean filter_get_operator_case (xmmsv_coll_t *coll, gboolean *val);
117 
118 static void build_match_table (gpointer key, gpointer value, gpointer udata);
119 static gboolean find_unchecked (gpointer name, gpointer value, gpointer udata);
120 static void build_list_matches (gpointer key, gpointer value, gpointer udata);
121 
122 static gboolean xmms_collection_media_match (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
123 static gboolean xmms_collection_media_match_operand (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
124 static gboolean xmms_collection_media_match_reference (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table, const gchar *refname, const gchar *refns);
125 static gboolean xmms_collection_media_filter_has (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
126 static gboolean xmms_collection_media_filter_equals (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
127 static gboolean xmms_collection_media_filter_match (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
128 static gboolean xmms_collection_media_filter_smaller (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
129 static gboolean xmms_collection_media_filter_greater (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
130 
131 static xmmsv_coll_t * xmms_collection_client_get (xmms_coll_dag_t *dag, const gchar *collname, const gchar *namespace, xmms_error_t *error);
132 static GList * xmms_collection_client_list (xmms_coll_dag_t *dag, const gchar *namespace, xmms_error_t *error);
133 static void xmms_collection_client_save (xmms_coll_dag_t *dag, const gchar *name, const gchar *namespace, xmmsv_coll_t *coll, xmms_error_t *error);
134 static void xmms_collection_client_remove (xmms_coll_dag_t *dag, const gchar *collname, const gchar *namespace, xmms_error_t *error);
135 static GList * xmms_collection_client_find (xmms_coll_dag_t *dag, gint32 mid, const gchar *namespace, xmms_error_t *error);
136 static void xmms_collection_client_rename (xmms_coll_dag_t *dag, const gchar *from_name, const gchar *to_name, const gchar *namespace, xmms_error_t *error);
137 
138 static GList * xmms_collection_client_query_infos (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, gint32 lim_start, gint32 lim_len, xmmsv_t *order, xmmsv_t *fetch, xmmsv_t *group, xmms_error_t *err);
139 static GList * xmms_collection_client_query_ids (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, gint32 lim_start, gint32 lim_len, xmmsv_t *order, xmms_error_t *err);
140 static xmmsv_coll_t *xmms_collection_client_idlist_from_pls (xmms_coll_dag_t *dag, const gchar *mediainfo, xmms_error_t *err);
141 static void xmms_collection_client_sync (xmms_coll_dag_t *dag, xmms_error_t *err);
142 
143 
144 XMMS_CMD_DEFINE (collection_get, xmms_collection_client_get, xmms_coll_dag_t *, COLL, STRING, STRING);
145 XMMS_CMD_DEFINE (collection_list, xmms_collection_client_list, xmms_coll_dag_t *, LIST, STRING, NONE);
146 XMMS_CMD_DEFINE3 (collection_save, xmms_collection_client_save, xmms_coll_dag_t *, NONE, STRING, STRING, COLL);
147 XMMS_CMD_DEFINE (collection_remove, xmms_collection_client_remove, xmms_coll_dag_t *, NONE, STRING, STRING);
148 XMMS_CMD_DEFINE (collection_find, xmms_collection_client_find, xmms_coll_dag_t *, LIST, INT32, STRING);
149 XMMS_CMD_DEFINE3 (collection_rename, xmms_collection_client_rename, xmms_coll_dag_t *, NONE, STRING, STRING, STRING);
150 XMMS_CMD_DEFINE (collection_from_pls, xmms_collection_client_idlist_from_pls, xmms_coll_dag_t *, COLL, STRING, NONE);
151 XMMS_CMD_DEFINE (collection_sync, xmms_collection_client_sync, xmms_coll_dag_t *, NONE, NONE, NONE);
152 
153 
154 XMMS_CMD_DEFINE4 (query_ids, xmms_collection_client_query_ids, xmms_coll_dag_t *, LIST, COLL, INT32, INT32, LIST);
155 XMMS_CMD_DEFINE6 (query_infos, xmms_collection_client_query_infos, xmms_coll_dag_t *, LIST, COLL, INT32, INT32, LIST, LIST, LIST);
156 
157 
158 GTree *
160  const gchar *plname, const gchar *namespace)
161 {
162  GTree *dict;
163 
164  dict = g_tree_new_full ((GCompareDataFunc) strcmp, NULL,
165  NULL, (GDestroyNotify)xmmsv_unref);
166 
167  g_tree_insert (dict, (gpointer) "type", xmmsv_new_int (type));
168  g_tree_insert (dict, (gpointer) "name", xmmsv_new_string (plname));
169  g_tree_insert (dict, (gpointer) "namespace", xmmsv_new_string (namespace));
170 
171  return dict;
172 }
173 
174 void
176 {
177  g_return_if_fail (colldag);
178  g_return_if_fail (dict);
179 
180  xmms_object_emit_f (XMMS_OBJECT (colldag),
183  dict);
184 
185  g_tree_destroy (dict);
186 }
187 
188 #define XMMS_COLLECTION_CHANGED_MSG(type, name, namespace) xmms_collection_changed_msg_send (dag, xmms_collection_changed_msg_new (type, name, namespace))
189 
190 
191 /** @defgroup Collection Collection
192  * @ingroup XMMSServer
193  * @brief This is the collection manager.
194  *
195  * The set of collections is stored as a DAG of collection operators.
196  * Each collection namespace contains a list of saved collections,
197  * with a pointer to the node in the graph.
198  * @{
199  */
200 
201 /** Collection DAG structure */
202 
203 struct xmms_coll_dag_St {
204  xmms_object_t object;
205 
206  /* Ref to the playlist object, needed to notify it when a playlist changes */
207  xmms_playlist_t *playlist;
208 
209  GHashTable *collrefs[XMMS_COLLECTION_NUM_NAMESPACES];
210 
211  GMutex *mutex;
212 
213 };
214 
215 static void
216 coll_sync_cb (xmms_object_t *object, xmmsv_t *val, gpointer udata)
217 {
219 }
220 
221 /** Initializes a new xmms_coll_dag_t.
222  *
223  * @returns The newly allocated collection DAG.
224  */
227 {
228  gint i;
229  xmms_coll_dag_t *ret;
231 
232  ret = xmms_object_new (xmms_coll_dag_t, xmms_collection_destroy);
233  ret->mutex = g_mutex_new ();
234  ret->playlist = playlist;
235 
236  xmms_coll_sync_init (ret);
237 
238  for (i = 0; i < XMMS_COLLECTION_NUM_NAMESPACES; ++i) {
239  ret->collrefs[i] = g_hash_table_new_full (g_str_hash, g_str_equal,
240  g_free, coll_unref);
241  }
242 
244 
247 
248  /* Connection coll_sync_cb to some signals */
251  coll_sync_cb, ret);
252 
253  /* FIXME: These signals should trigger COLLECTION_CHANGED */
254  xmms_object_connect (XMMS_OBJECT (playlist),
256  coll_sync_cb, ret);
257 
258  xmms_object_connect (XMMS_OBJECT (playlist),
260  coll_sync_cb, ret);
261 
262  xmms_object_connect (XMMS_OBJECT (playlist),
264  coll_sync_cb, ret);
265 
266 
269  XMMS_CMD_FUNC (collection_get));
270 
273  XMMS_CMD_FUNC (collection_list));
274 
277  XMMS_CMD_FUNC (collection_save));
278 
281  XMMS_CMD_FUNC (collection_remove));
282 
285  XMMS_CMD_FUNC (collection_find));
286 
289  XMMS_CMD_FUNC (collection_rename));
290 
293  XMMS_CMD_FUNC (query_ids));
294 
297  XMMS_CMD_FUNC (query_infos));
298 
301  XMMS_CMD_FUNC (collection_from_pls));
302 
305  XMMS_CMD_FUNC (collection_sync));
306 
308 
309  f = _xmms_stream_type_new (NULL,
311  "application/x-xmms2-playlist-entries",
313  global_stream_type = g_list_prepend (NULL, f);
314 
315  return ret;
316 }
317 
318 static void
319 add_metadata_from_tree (const gchar *key, xmmsv_t *value, gpointer user_data)
320 {
321  add_metadata_from_tree_user_data_t *ud = user_data;
322 
323  if (xmmsv_get_type (value) == XMMSV_TYPE_INT32) {
324  gint iv;
325  xmmsv_get_int (value, &iv);
326  xmms_medialib_entry_property_set_int_source (ud->session, ud->entry,
327  key,
328  iv,
329  ud->src);
330  } else if (xmmsv_get_type (value) == XMMSV_TYPE_STRING) {
331  const gchar *sv;
332  xmmsv_get_string (value, &sv);
333  xmms_medialib_entry_property_set_str_source (ud->session, ud->entry,
334  key,
335  sv,
336  ud->src);
337  }
338 }
339 
340 
341 /** Create a idlist from a playlist file
342  * @param dag The collection DAG.
343  * @param path URL to the playlist file
344  * @param err If error occurs, a message is stored in this variable.
345  * @returns A idlist
346  */
347 static xmmsv_coll_t *
348 xmms_collection_client_idlist_from_pls (xmms_coll_dag_t *dag, const gchar *path,
349  xmms_error_t *err)
350 {
351  xmms_xform_t *xform;
352  GList *lst, *n;
353  xmmsv_coll_t *coll;
354  xmms_medialib_session_t *session;
355  guint src;
356  const gchar *buf;
357 
358  /* we don't want any effects for playlist, so just report we're rehashing */
359  xform = xmms_xform_chain_setup_url (0, path, global_stream_type, TRUE);
360 
361  if (!xform) {
362  xmms_error_set (err, XMMS_ERROR_NO_SAUSAGE, "We can't handle this type of playlist or URL");
363  return NULL;
364  }
365 
366  lst = xmms_xform_browse_method (xform, "/", err);
367  if (xmms_error_iserror (err)) {
368  xmms_object_unref (xform);
369  return NULL;
370  }
371 
373  session = xmms_medialib_begin_write ();
374  src = xmms_medialib_source_to_id (session, "plugin/playlist");
375 
376  n = lst;
377  while (n) {
378  xmms_medialib_entry_t entry;
379 
380  xmmsv_t *a = n->data;
381  xmmsv_t *b;
382 
383  if (!xmmsv_dict_get (a, "realpath", &b)) {
384  xmms_log_error ("Playlist plugin did not set realpath; probably a bug in plugin");
385  xmmsv_unref (a);
386  n = g_list_delete_link (n, n);
387  continue;
388  }
389 
390  xmmsv_get_string (b, &buf);
391  entry = xmms_medialib_entry_new_encoded (session, buf, err);
392  xmmsv_dict_remove (a, "realpath");
393  xmmsv_dict_remove (a, "path");
394 
395  if (entry) {
397  udata.session = session;
398  udata.entry = entry;
399  udata.src = src;
400 
401  xmmsv_dict_foreach(a, add_metadata_from_tree, &udata);
402 
403  xmmsv_coll_idlist_append (coll, entry);
404  } else {
405  xmmsv_get_string (b, &buf);
406  xmms_log_error ("couldn't add %s to collection!", buf);
407  }
408 
409  xmmsv_unref (a);
410  n = g_list_delete_link (n, n);
411  }
412 
413  xmms_medialib_end (session);
414  xmms_object_unref (xform);
415 
416  return coll;
417 }
418 
419 /** Remove the given collection from the DAG.
420 *
421 * If to be removed from ALL namespaces, then all matching collections are removed.
422 *
423 * @param dag The collection DAG.
424 * @param name The name of the collection to remove.
425 * @param namespace The namespace where the collection to remove is (can be ALL).
426 * @param err If an error occurs, a message is stored in it.
427 * @returns True on success, false otherwise.
428 */
429 void
430 xmms_collection_client_remove (xmms_coll_dag_t *dag, const gchar *name,
431  const gchar *namespace, xmms_error_t *err)
432 {
433  guint nsid;
434  gboolean retval = FALSE;
435  guint i;
436 
437  nsid = xmms_collection_get_namespace_id (namespace);
438  if (nsid == XMMS_COLLECTION_NSID_INVALID) {
439  xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection namespace");
440  return;
441  }
442 
443  g_mutex_lock (dag->mutex);
444 
445  /* Unreference the matching collection(s) */
446  if (nsid == XMMS_COLLECTION_NSID_ALL) {
447  for (i = 0; i < XMMS_COLLECTION_NUM_NAMESPACES; ++i) {
448  retval = xmms_collection_unreference (dag, name, i) || retval;
449  }
450  } else {
451  retval = xmms_collection_unreference (dag, name, nsid);
452  }
453 
454  g_mutex_unlock (dag->mutex);
455 
456  if (retval == FALSE) {
457  xmms_error_set (err, XMMS_ERROR_NOENT, "Failed to remove this collection!");
458  }
459 
460 }
461 
462 /** Save the given collection in the DAG under the given name in the given namespace.
463  *
464  * @param dag The collection DAG in which to save the collection.
465  * @param name The name under which to save the collection.
466  * @param namespace The namespace in which to save th collection.
467  * @param coll The collection structure to save.
468  * @param err If an error occurs, a message is stored in it.
469  * @returns True on success, false otherwise.
470  */
471 void
472 xmms_collection_client_save (xmms_coll_dag_t *dag, const gchar *name, const gchar *namespace,
473  xmmsv_coll_t *coll, xmms_error_t *err)
474 {
475  xmmsv_coll_t *existing;
476  guint nsid;
477  const gchar *alias;
478  gchar *newkey = NULL;
479 
480  nsid = xmms_collection_get_namespace_id (namespace);
481  if (nsid == XMMS_COLLECTION_NSID_INVALID) {
482  xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection namespace");
483  return;
484  } else if (nsid == XMMS_COLLECTION_NSID_ALL) {
485  xmms_error_set (err, XMMS_ERROR_GENERIC, "cannot save collection in all namespaces");
486  return;
487  }
488 
489  /* Validate collection structure */
490  if (!xmms_collection_validate (dag, coll, name, namespace)) {
491  xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection structure");
492  return;
493  }
494 
495  g_mutex_lock (dag->mutex);
496 
497  /* Unreference previously saved collection */
498  existing = xmms_collection_get_pointer (dag, name, nsid);
499  if (existing != NULL) {
500  /* Rebind reference pointers to the new collection */
501  coll_rebind_infos_t infos = { name, namespace, existing, coll };
502  xmms_collection_apply_to_all_collections (dag, rebind_references, &infos);
503  }
504 
505  /* Link references in newly saved collection to actual operators */
507 
508  /* Update existing collection in the table */
509  if (existing != NULL) {
510  while ((alias = xmms_collection_find_alias (dag, nsid,
511  existing, NULL)) != NULL) {
512  newkey = g_strdup (alias);
513 
514  /* update all pairs pointing to the old coll */
515  xmms_collection_dag_replace (dag, nsid, newkey, coll);
516  xmmsv_coll_ref (coll);
517 
519  newkey,
520  namespace);
521  }
522 
523  /* Save new collection in the table */
524  } else {
525  newkey = g_strdup (name);
526  xmms_collection_dag_replace (dag, nsid, newkey, coll);
527  xmmsv_coll_ref (coll);
528 
530  newkey,
531  namespace);
532  }
533 
534  g_mutex_unlock (dag->mutex);
535 
536  /* If updating a playlist, trigger PLAYLIST_CHANGED */
537  if (nsid == XMMS_COLLECTION_NSID_PLAYLISTS) {
538  XMMS_PLAYLIST_COLLECTION_CHANGED_MSG (dag->playlist, newkey);
539  }
540 
541 }
542 
543 
544 /** Retrieve the structure of a given collection.
545  *
546  * If looking in ALL namespaces, only the collection first found is returned!
547  *
548  * @param dag The collection DAG.
549  * @param name The name of the collection to retrieve.
550  * @param namespace The namespace in which to look for the collection.
551  * @param err If an error occurs, a message is stored in it.
552  * @returns The collection structure if found, NULL otherwise.
553  */
554 xmmsv_coll_t *
555 xmms_collection_client_get (xmms_coll_dag_t *dag, const gchar *name,
556  const gchar *namespace, xmms_error_t *err)
557 {
558  xmmsv_coll_t *coll = NULL;
559  guint nsid;
560 
561  nsid = xmms_collection_get_namespace_id (namespace);
562  if (nsid == XMMS_COLLECTION_NSID_INVALID) {
563  xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection namespace");
564  return NULL;
565  }
566 
567  g_mutex_lock (dag->mutex);
568 
569  coll = xmms_collection_get_pointer (dag, name, nsid);
570 
571  /* Not found! */
572  if (coll == NULL) {
573  xmms_error_set (err, XMMS_ERROR_NOENT, "no such collection");
574 
575  /* New reference, will be freed after being put in the return message */
576  } else {
577  xmmsv_coll_ref (coll);
578  }
579 
580  g_mutex_unlock (dag->mutex);
581 
582  return coll;
583 }
584 
585 
586 /** Synchronize collection data to the database (i.e. to disk).
587  *
588  * @param dag The collection DAG.
589  * @param err If an error occurs, a message is stored in it.
590  */
591 
592 void
594 {
595  g_return_if_fail (dag);
596 
597  g_mutex_lock (dag->mutex);
598 
600 
601  g_mutex_unlock (dag->mutex);
602 }
603 
604 
605 void
606 xmms_collection_client_sync (xmms_coll_dag_t *dag, xmms_error_t *err)
607 {
608  xmms_collection_sync (dag);
609 }
610 
611 
612 /** Lists the collections in the given namespace.
613  *
614  * @param dag The collection DAG.
615  * @param namespace The namespace to list collections from (can be ALL).
616  * @param err If an error occurs, a message is stored in it.
617  * @returns A newly allocated GList with the list of collection names.
618  * Remember that it is only the LIST that is copied. Not the entries.
619  * The entries are however referenced, and must be unreffed!
620  */
621 GList *
622 xmms_collection_client_list (xmms_coll_dag_t *dag, const gchar *namespace,
623  xmms_error_t *err)
624 {
625  GList *r = NULL;
626  guint nsid;
627 
628  nsid = xmms_collection_get_namespace_id (namespace);
629  if (nsid == XMMS_COLLECTION_NSID_INVALID) {
630  xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection namespace");
631  return NULL;
632  }
633 
634  g_mutex_lock (dag->mutex);
635 
636  /* Get the list of collections in the given namespace */
637  xmms_collection_foreach_in_namespace (dag, nsid, prepend_key_string, &r);
638 
639  g_mutex_unlock (dag->mutex);
640 
641  return r;
642 }
643 
644 
645 /** Find all collections in the given namespace that contain a given media.
646  *
647  * @param dag The collection DAG.
648  * @param mid The id of the media.
649  * @param namespace The namespace in which to look for collections.
650  * @param err If an error occurs, a message is stored in it.
651  * @returns A newly allocated GList with the names of the matching collections.
652  */
653 GList *
654 xmms_collection_client_find (xmms_coll_dag_t *dag, gint32 mid, const gchar *namespace,
655  xmms_error_t *err)
656 {
657  GHashTable *mediainfo;
658  GList *ret = NULL;
659  guint nsid;
660  gchar *open_name;
661  GHashTable *match_table;
662  xmmsv_coll_t *coll;
663 
664  /* Verify namespace */
665  nsid = xmms_collection_get_namespace_id (namespace);
666  if (nsid == XMMS_COLLECTION_NSID_INVALID) {
667  xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection namespace");
668  return NULL;
669  }
670  if (nsid == XMMS_COLLECTION_NSID_ALL) {
671  xmms_error_set (err, XMMS_ERROR_INVAL, "cannot search in all namespaces");
672  return NULL;
673  }
674 
675  /* Prepare the match table of all collections for the given namespace */
676  match_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
677  xmms_collection_foreach_in_namespace (dag, nsid, build_match_table, match_table);
678 
679  /* Get all infos for the given mid */
680  mediainfo = xmms_collection_media_info (mid, err);
681 
682  /* While not all collections have been checked, check next */
683  while (g_hash_table_find (match_table, find_unchecked, &open_name) != NULL) {
684  coll_find_state_t *match = g_new (coll_find_state_t, 1);
685  coll = xmms_collection_get_pointer (dag, open_name, nsid);
686  if (xmms_collection_media_match (dag, mediainfo, coll, nsid, match_table)) {
688  } else {
690  }
691  g_hash_table_replace (match_table, g_strdup (open_name), match);
692  }
693 
694  /* List matching collections */
695  g_hash_table_foreach (match_table, build_list_matches, &ret);
696  g_hash_table_destroy (match_table);
697 
698  g_hash_table_destroy (mediainfo);
699 
700  return ret;
701 }
702 
703 
704 /** Rename a collection in a given namespace.
705  *
706  * @param dag The collection DAG.
707  * @param from_name The name of the collection to rename.
708  * @param to_name The new name of the collection.
709  * @param namespace The namespace to consider (cannot be ALL).
710  * @param err If an error occurs, a message is stored in it.
711  * @return True if a collection was found and renamed.
712  */
713 void
714 xmms_collection_client_rename (xmms_coll_dag_t *dag, const gchar *from_name,
715  const gchar *to_name, const gchar *namespace,
716  xmms_error_t *err)
717 {
718  gboolean retval;
719  guint nsid;
720  xmmsv_coll_t *from_coll, *to_coll;
721 
722  nsid = xmms_collection_get_namespace_id (namespace);
723  if (nsid == XMMS_COLLECTION_NSID_INVALID) {
724  xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection namespace");
725  return;
726  } else if (nsid == XMMS_COLLECTION_NSID_ALL) {
727  xmms_error_set (err, XMMS_ERROR_GENERIC, "cannot rename collection in all namespaces");
728  return;
729  }
730 
731  g_mutex_lock (dag->mutex);
732 
733  from_coll = xmms_collection_get_pointer (dag, from_name, nsid);
734  to_coll = xmms_collection_get_pointer (dag, to_name, nsid);
735 
736  /* Input validation */
737  if (from_coll == NULL) {
738  xmms_error_set (err, XMMS_ERROR_NOENT, "no such collection");
739  retval = FALSE;
740 
741  } else if (to_coll != NULL) {
742  xmms_error_set (err, XMMS_ERROR_NOENT, "a collection already exists with the target name");
743  retval = FALSE;
744 
745  /* Update collection name everywhere */
746  } else {
747  GTree *dict;
748 
749  /* insert new pair in hashtable */
750  xmms_collection_dag_replace (dag, nsid, g_strdup (to_name), from_coll);
751  xmmsv_coll_ref (from_coll);
752 
753  /* remove old pair from hashtable */
754  g_hash_table_remove (dag->collrefs[nsid], from_name);
755 
756  /* update name in all reference operators */
757  coll_rename_infos_t infos = { from_name, to_name, namespace };
758  xmms_collection_apply_to_all_collections (dag, rename_references, &infos);
759 
760  /* Send _RENAME signal */
762  from_name, namespace);
763  g_tree_insert (dict, (gpointer) "newname", xmmsv_new_string (to_name));
765 
766  retval = TRUE;
767  }
768 
769  g_mutex_unlock (dag->mutex);
770 
771 }
772 
773 
774 /** Find the ids of the media matched by a collection.
775  *
776  * @param dag The collection DAG.
777  * @param coll The collection used to match media.
778  * @param lim_start The beginning index of the LIMIT statement (0 to disable).
779  * @param lim_len The number of entries of the LIMIT statement (0 to disable).
780  * @param order The list of properties to order by (empty to disable).
781  * @param err If an error occurs, a message is stored in it.
782  * @return A list of media ids.
783  */
784 GList *
786  gint32 lim_start, gint32 lim_len, xmmsv_t *order,
787  xmms_error_t *err)
788 {
789  GList *res, *n;
790  xmmsv_t *fetch, *group, *idval;
791 
792  /* no grouping, fetch only id */
793  group = xmmsv_new_list ();
794  fetch = xmmsv_new_list ();
795  idval = xmmsv_new_string ("id");
796  xmmsv_list_append (fetch, idval);
797 
798  res = xmms_collection_client_query_infos (dag, coll, lim_start, lim_len, order, fetch, group, err);
799 
800  /* FIXME: get an uint list directly ! (we're getting ints here actually) */
801  for (n = res; n; n = n->next) {
803  xmmsv_t *id_val, *cmdval = n->data;
804 
805  xmmsv_dict_get (cmdval, "id", &id_val);
806  xmmsv_get_int (id_val, &id);
807  n->data = xmmsv_new_int (id);
808 
809  xmmsv_unref (cmdval);
810  }
811 
812  xmmsv_unref (group);
813  xmmsv_unref (fetch);
814  xmmsv_unref (idval);
815 
816  return res;
817 }
818 
819 
820 GList *
821 xmms_collection_client_query_ids (xmms_coll_dag_t *dag, xmmsv_coll_t *coll,
822  gint32 lim_start, gint32 lim_len, xmmsv_t *order,
823  xmms_error_t *err)
824 {
825  return xmms_collection_query_ids (dag, coll, lim_start, lim_len, order, err);
826 }
827 /** Find the properties of the media matched by a collection.
828  *
829  * @param dag The collection DAG.
830  * @param coll The collection used to match media.
831  * @param lim_start The beginning index of the LIMIT statement (0 to disable).
832  * @param lim_len The number of entries of the LIMIT statement (0 to disable).
833  * @param order The list of properties to order by, prefix by '-' to invert (empty to disable).
834  * @param fetch The list of properties to be retrieved.
835  * @param group The list of properties to group by (empty to disable).
836  * @param err If an error occurs, a message is stored in it.
837  * @return A list of property dicts for each entry.
838  */
839 GList *
840 xmms_collection_client_query_infos (xmms_coll_dag_t *dag, xmmsv_coll_t *coll,
841  gint32 lim_start, gint32 lim_len, xmmsv_t *order,
842  xmmsv_t *fetch, xmmsv_t *group, xmms_error_t *err)
843 {
844  GList *res = NULL;
845  GString *query;
846 
847  /* check that fetch is not empty */
848  if (xmmsv_list_get_size (fetch) == 0) {
849  xmms_error_set (err, XMMS_ERROR_INVAL, "fetch list must not be empty!");
850  return NULL;
851  }
852 
853  /* check for invalid property strings */
854  if (!check_string_list (order)) {
855  xmms_error_set (err, XMMS_ERROR_NOENT, "invalid order list!");
856  return NULL;
857  }
858  if (!check_string_list (fetch)) {
859  xmms_error_set (err, XMMS_ERROR_NOENT, "invalid fetch list!");
860  return NULL;
861  }
862  if (!check_string_list (group)) {
863  xmms_error_set (err, XMMS_ERROR_NOENT, "invalid group list!");
864  return NULL;
865  }
866 
867  /* validate the collection to query */
868  if (!xmms_collection_validate (dag, coll, NULL, NULL)) {
869  if (err) {
870  xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection structure");
871  }
872  return NULL;
873  }
874 
875  g_mutex_lock (dag->mutex);
876 
877  query = xmms_collection_get_query (dag, coll, lim_start, lim_len,
878  order, fetch, group);
879 
880  g_mutex_unlock (dag->mutex);
881 
882  XMMS_DBG ("COLLECTIONS: query_infos with %s", query->str);
883 
884  /* Run the query */
886  res = xmms_medialib_select (session, query->str, err);
887  xmms_medialib_end (session);
888 
889  g_string_free (query, TRUE);
890 
891  return res;
892 }
893 
894 /**
895  * Update a reference to point to a new collection.
896  *
897  * @param dag The collection DAG.
898  * @param name The name of the reference to update.
899  * @param nsid The namespace in which to locate the reference.
900  * @param newtarget The new collection pointed to by the reference.
901  */
902 void
904  guint nsid, xmmsv_coll_t *newtarget)
905 {
906  xmms_collection_dag_replace (dag, nsid, g_strdup (name), newtarget);
907  xmmsv_coll_ref (newtarget);
908 }
909 
910 /** Update the DAG to update the value of the pair with the given key. */
911 void
914  gchar *key, xmmsv_coll_t *newcoll)
915 {
916  g_hash_table_replace (dag->collrefs[nsid], key, newcoll);
917 }
918 
919 /** Find the collection structure corresponding to the given name in the given namespace.
920  *
921  * @param dag The collection DAG.
922  * @param collname The name of the collection to find.
923  * @param nsid The namespace id.
924  * @returns The collection structure if found, NULL otherwise.
925  */
926 xmmsv_coll_t *
927 xmms_collection_get_pointer (xmms_coll_dag_t *dag, const gchar *collname,
928  guint nsid)
929 {
930  gint i;
931  xmmsv_coll_t *coll = NULL;
932 
933  if (nsid == XMMS_COLLECTION_NSID_ALL) {
934  for (i = 0; i < XMMS_COLLECTION_NUM_NAMESPACES && coll == NULL; ++i) {
935  coll = g_hash_table_lookup (dag->collrefs[i], collname);
936  }
937  } else {
938  coll = g_hash_table_lookup (dag->collrefs[nsid], collname);
939  }
940 
941  return coll;
942 }
943 
944 /** Extract an attribute from a collection as an integer.
945  *
946  * @param coll The collection to extract the attribute from.
947  * @param attrname The name of the attribute.
948  * @param val The integer value of the attribute will be saved in this pointer.
949  * @return TRUE if attribute correctly read, FALSE otherwise
950  */
951 gboolean
952 xmms_collection_get_int_attr (xmmsv_coll_t *coll, const gchar *attrname, gint *val)
953 {
954  gboolean retval = FALSE;
955  gint buf;
956  gchar *str;
957  gchar *endptr;
958 
959  if (xmmsv_coll_attribute_get (coll, attrname, &str)) {
960  buf = strtol (str, &endptr, 10);
961 
962  /* Valid integer string */
963  if (*endptr == '\0') {
964  *val = buf;
965  retval = TRUE;
966  }
967  }
968 
969  return retval;
970 }
971 
972 /** Set the attribute of a collection as an integer.
973  *
974  * @param coll The collection in which to set the attribute.
975  * @param attrname The name of the attribute.
976  * @param newval The new value of the attribute.
977  * @return TRUE if attribute successfully saved, FALSE otherwise.
978  */
979 gboolean
980 xmms_collection_set_int_attr (xmmsv_coll_t *coll, const gchar *attrname,
981  gint newval)
982 {
983  gboolean retval = FALSE;
984  gchar str[XMMS_MAX_INT_ATTRIBUTE_LEN + 1];
985  gint written;
986 
987  written = g_snprintf (str, sizeof (str), "%d", newval);
988  if (written < XMMS_MAX_INT_ATTRIBUTE_LEN) {
989  xmmsv_coll_attribute_set (coll, attrname, str);
990  retval = TRUE;
991  }
992 
993  return retval;
994 }
995 
996 
997 /**
998  * Reverse-search the list of collections in the given namespace to
999  * find the first pair whose value matches the argument. If key is
1000  * not NULL, any pair with the same key will be ignored.
1001  *
1002  * @param dag The collection DAG.
1003  * @param nsid The id of the namespace to consider.
1004  * @param value The value of the pair to find.
1005  * @param key If not NULL, ignore any pair with that key.
1006  * @return The key of the found pair.
1007  */
1008 const gchar *
1010  xmmsv_coll_t *value, const gchar *key)
1011 {
1012  const gchar *otherkey = NULL;
1013  coll_table_pair_t search_pair = { key, value };
1014 
1015  if (g_hash_table_find (dag->collrefs[nsid], value_match_save_key,
1016  &search_pair) != NULL) {
1017  otherkey = search_pair.key;
1018  }
1019 
1020  return otherkey;
1021 }
1022 
1023 
1024 /**
1025  * Get a random media entry from the given collection.
1026  *
1027  * @param dag The collection DAG.
1028  * @param source The collection to query.
1029  * @return A random media from the source collection, or 0 if none found.
1030  */
1033 {
1034  GList *res;
1035  xmms_medialib_entry_t mid = 0;
1036  xmmsv_t *rorder = xmmsv_new_list ();
1037  xmmsv_t *randval = xmmsv_new_string ("~RANDOM()");
1038 
1039  /* FIXME: Temporary hack to allow custom ordering functions */
1040  xmmsv_list_append (rorder, randval);
1041 
1042  res = xmms_collection_query_ids (dag, source, 0, 1, rorder, NULL);
1043 
1044  if (res != NULL) {
1045  xmmsv_t *val = (xmmsv_t *) res->data;
1046  xmmsv_get_int (val, &mid);
1047  xmmsv_unref (val);
1048  g_list_free (res);
1049  }
1050 
1051  xmmsv_unref (rorder);
1052  xmmsv_unref (randval);
1053 
1054  return mid;
1055 }
1056 
1057 /** @} */
1058 
1059 
1060 
1061 /** Free the collection DAG and other memory in the xmms_coll_dag_t
1062  *
1063  * This will free all collections in the DAG!
1064  */
1065 static void
1066 xmms_collection_destroy (xmms_object_t *object)
1067 {
1068  gint i;
1069  xmms_coll_dag_t *dag = (xmms_coll_dag_t *)object;
1070 
1071  g_return_if_fail (dag);
1072 
1075 
1076  g_mutex_free (dag->mutex);
1077 
1078  for (i = 0; i < XMMS_COLLECTION_NUM_NAMESPACES; ++i) {
1079  g_hash_table_destroy (dag->collrefs[i]); /* dag is freed here */
1080  }
1081 
1083 
1085 }
1086 
1087 /** Validate the given collection against a DAG.
1088  *
1089  * @param dag The collection DAG.
1090  * @param coll The collection to validate.
1091  * @param save_name The name under which the collection will be saved (NULL
1092  * if none).
1093  * @param save_namespace The namespace in which the collection will be
1094  * saved (NULL if none).
1095  * @returns True if the collection is valid, false otherwise.
1096  */
1097 static gboolean
1098 xmms_collection_validate (xmms_coll_dag_t *dag, xmmsv_coll_t *coll,
1099  const gchar *save_name, const gchar *save_namespace)
1100 {
1101  /* Special validation checks for the Playlists namespace */
1102  if (save_namespace != NULL &&
1103  strcmp (save_namespace, XMMS_COLLECTION_NS_PLAYLISTS) == 0) {
1104  /* only accept idlists */
1108  return FALSE;
1109  }
1110  }
1111 
1112  /* Standard checking of the whole coll DAG */
1113  return xmms_collection_validate_recurs (dag, coll, save_name,
1114  save_namespace);
1115 }
1116 
1117 /**
1118  * Internal recursive validation function used to validate the whole
1119  * graph of a collection.
1120  */
1121 static gboolean
1122 xmms_collection_validate_recurs (xmms_coll_dag_t *dag, xmmsv_coll_t *coll,
1123  const gchar *save_name, const gchar *save_namespace)
1124 {
1125  guint num_operands = 0;
1126  xmmsv_coll_t *op, *ref;
1127  gchar *attr, *attr2;
1128  gboolean valid = TRUE;
1129  xmmsv_coll_type_t type;
1131 
1132  /* count operands */
1133  num_operands = xmmsv_list_get_size (xmmsv_coll_operands_get (coll));
1134 
1135  /* analyse by type */
1136  type = xmmsv_coll_get_type (coll);
1137  switch (type) {
1139  /* zero or one (bound in DAG) operand */
1140  if (num_operands > 1) {
1141  return FALSE;
1142  }
1143 
1144  /* check if referenced collection exists */
1145  xmmsv_coll_attribute_get (coll, "reference", &attr);
1146  if (attr == NULL) {
1147  return FALSE;
1148  } else if (strcmp (attr, "All Media") != 0) {
1149  xmmsv_coll_attribute_get (coll, "namespace", &attr2);
1150 
1151  if (attr2 == NULL) {
1152  return FALSE;
1153  }
1154 
1155  nsid = xmms_collection_get_namespace_id (attr2);
1156  if (nsid == XMMS_COLLECTION_NSID_INVALID) {
1157  return FALSE;
1158  }
1159 
1160  g_mutex_lock (dag->mutex);
1161  ref = xmms_collection_get_pointer (dag, attr, nsid);
1162  if (ref == NULL) {
1163  g_mutex_unlock (dag->mutex);
1164  return FALSE;
1165  }
1166 
1167  if (save_name && save_namespace) {
1168  /* self-reference is of course forbidden */
1169  if (strcmp (attr, save_name) == 0 &&
1170  strcmp (attr2, save_namespace) == 0) {
1171 
1172  g_mutex_unlock (dag->mutex);
1173  return FALSE;
1174 
1175  /* check if the referenced coll references this one (loop!) */
1176  } else if (xmms_collection_has_reference_to (dag, ref, save_name,
1177  save_namespace)) {
1178  g_mutex_unlock (dag->mutex);
1179  return FALSE;
1180  }
1181  }
1182 
1183  g_mutex_unlock (dag->mutex);
1184  } else {
1185  /* "All Media" reference, so no referenced coll pointer */
1186  ref = NULL;
1187  }
1188 
1189  /* ensure that the operand is consistent with the reference infos */
1190  if (num_operands == 1) {
1191  xmmsv_t *val;
1192  xmmsv_list_get (xmmsv_coll_operands_get (coll), 0, &val);
1193  xmmsv_get_coll (val, &op);
1194 
1195  if (op != ref) {
1196  return FALSE;
1197  }
1198  }
1199  break;
1200 
1203  /* need operand(s) */
1204  if (num_operands == 0) {
1205  return FALSE;
1206  }
1207  break;
1208 
1210  /* one operand */
1211  if (num_operands != 1) {
1212  return FALSE;
1213  }
1214  break;
1215 
1217  /* one operand */
1218  if (num_operands != 1) {
1219  return FALSE;
1220  }
1221 
1222  /* "field" attribute */
1223  /* with valid value */
1224  if (!xmmsv_coll_attribute_get (coll, "field", &attr)) {
1225  return FALSE;
1226  }
1227  break;
1228 
1233  /* one operand */
1234  if (num_operands != 1) {
1235  return FALSE;
1236  }
1237 
1238  /* "field"/"value" attributes */
1239  /* with valid values */
1240  if (!xmmsv_coll_attribute_get (coll, "field", &attr)) {
1241  return FALSE;
1242  }
1243  /* FIXME: valid fields?
1244  else if (...) {
1245  return FALSE;
1246  }
1247  */
1248 
1249  if (!xmmsv_coll_attribute_get (coll, "value", &attr)) {
1250  return FALSE;
1251  }
1252  break;
1253 
1256  /* no operand */
1257  if (num_operands > 0) {
1258  return FALSE;
1259  }
1260  break;
1261 
1263  /* one operand */
1264  if (num_operands != 1) {
1265  return FALSE;
1266  }
1267  break;
1268 
1269  /* invalid type */
1270  default:
1271  return FALSE;
1272  break;
1273  }
1274 
1275 
1276  /* recurse in operands */
1277  if (num_operands > 0 && type != XMMS_COLLECTION_TYPE_REFERENCE) {
1278  xmmsv_list_iter_t *iter;
1280 
1281  for (xmmsv_list_iter_first (iter);
1282  valid && xmmsv_list_iter_valid (iter);
1283  xmmsv_list_iter_next (iter)) {
1284 
1285  xmmsv_t *val;
1286  xmmsv_list_iter_entry (iter, &val);
1287  xmmsv_get_coll (val, &op);
1288 
1289  if (!xmms_collection_validate_recurs (dag, op, save_name,
1290  save_namespace)) {
1291  valid = FALSE;
1292  }
1293  }
1294 
1296  }
1297 
1298  return valid;
1299 }
1300 
1301 /** Try to unreference a collection from a given namespace.
1302  *
1303  * @param dag The collection DAG.
1304  * @param name The name of the collection to remove.
1305  * @param nsid The namespace in which to look for the collection (yes, redundant).
1306  * @returns TRUE if a collection was removed, FALSE otherwise.
1307  */
1308 static gboolean
1309 xmms_collection_unreference (xmms_coll_dag_t *dag, const gchar *name, guint nsid)
1310 {
1311  xmmsv_coll_t *existing, *active_pl;
1312  gboolean retval = FALSE;
1313 
1314  existing = g_hash_table_lookup (dag->collrefs[nsid], name);
1315  active_pl = g_hash_table_lookup (dag->collrefs[XMMS_COLLECTION_NSID_PLAYLISTS],
1317 
1318  /* Unref if collection exists, and is not pointed at by _active playlist */
1319  if (existing != NULL && existing != active_pl) {
1320  const gchar *matchkey;
1321  const gchar *nsname = xmms_collection_get_namespace_string (nsid);
1322  coll_rebind_infos_t infos = { name, nsname, existing, NULL };
1323 
1324  /* FIXME: if reference pointed to by a label, we should update
1325  * the label to point to the ref'd operator instead ! */
1326 
1327  /* Strip all references to the deleted coll, bind operator directly */
1328  xmms_collection_apply_to_all_collections (dag, strip_references, &infos);
1329 
1330  /* Remove all pairs pointing to that collection */
1331  while ((matchkey = xmms_collection_find_alias (dag, nsid,
1332  existing, NULL)) != NULL) {
1333 
1335  matchkey,
1336  nsname);
1337 
1338  g_hash_table_remove (dag->collrefs[nsid], matchkey);
1339  }
1340 
1341  retval = TRUE;
1342  }
1343 
1344  return retval;
1345 }
1346 
1347 /** Find the namespace id corresponding to a namespace string.
1348  *
1349  * @param namespace The namespace string.
1350  * @returns The namespace id.
1351  */
1353 xmms_collection_get_namespace_id (const gchar *namespace)
1354 {
1355  guint nsid;
1356 
1357  if (strcmp (namespace, XMMS_COLLECTION_NS_ALL) == 0) {
1358  nsid = XMMS_COLLECTION_NSID_ALL;
1359  } else if (strcmp (namespace, XMMS_COLLECTION_NS_COLLECTIONS) == 0) {
1361  } else if (strcmp (namespace, XMMS_COLLECTION_NS_PLAYLISTS) == 0) {
1363  } else {
1365  }
1366 
1367  return nsid;
1368 }
1369 
1370 /** Find the namespace name (string) corresponding to a namespace id.
1371  *
1372  * @param nsid The namespace id.
1373  * @returns The namespace name (string).
1374  */
1375 const gchar *
1377 {
1378  const gchar *name;
1379 
1380  switch (nsid) {
1382  name = XMMS_COLLECTION_NS_ALL;
1383  break;
1386  break;
1389  break;
1390 
1392  default:
1393  name = NULL;
1394  break;
1395  }
1396 
1397  return name;
1398 }
1399 
1400 
1401 /** Check whether a collection structure contains a reference to a given collection.
1402  *
1403  * @param dag The collection DAG.
1404  * @param coll The collection to inspect for reference.
1405  * @param tg_name The name of the collection to find a reference to.
1406  * @param tg_ns The namespace of the collection to find a reference to.
1407  * @returns True if the collection contains a reference to the given
1408  * collection, false otherwise
1409  */
1410 static gboolean
1411 xmms_collection_has_reference_to (xmms_coll_dag_t *dag, xmmsv_coll_t *coll,
1412  const gchar *tg_name, const gchar *tg_ns)
1413 {
1414  coll_refcheck_t check = { tg_name, tg_ns, FALSE };
1415  xmms_collection_apply_to_collection (dag, coll, check_for_reference, &check);
1416 
1417  return check.found;
1418 }
1419 
1420 
1421 /** Apply a function to all the collections in a given namespace.
1422  *
1423  * @param dag The collection DAG.
1424  * @param nsid The namespace id.
1425  * @param f The function to apply to all the collections.
1426  * @param udata Additional user data parameter passed to the function.
1427  */
1428 void
1429 xmms_collection_foreach_in_namespace (xmms_coll_dag_t *dag, guint nsid, GHFunc f, void *udata)
1430 {
1431  gint i;
1432 
1433  if (nsid == XMMS_COLLECTION_NSID_ALL) {
1434  for (i = 0; i < XMMS_COLLECTION_NUM_NAMESPACES; ++i) {
1435  g_hash_table_foreach (dag->collrefs[i], f, udata);
1436  }
1437  } else if (nsid != XMMS_COLLECTION_NSID_INVALID) {
1438  g_hash_table_foreach (dag->collrefs[nsid], f, udata);
1439  }
1440 }
1441 
1442 /** Apply a function of type #FuncApplyToColl to all the collections in all namespaces.
1443  *
1444  * @param dag The collection DAG.
1445  * @param f The function to apply to all the collections.
1446  * @param udata Additional user data parameter passed to the function.
1447  */
1448 void
1450  FuncApplyToColl f, void *udata)
1451 {
1452  gint i;
1453  coll_call_infos_t callinfos = { dag, f, udata };
1454 
1455  for (i = 0; i < XMMS_COLLECTION_NUM_NAMESPACES; ++i) {
1456  g_hash_table_foreach (dag->collrefs[i], call_apply_to_coll, &callinfos);
1457  }
1458 }
1459 
1460 /** Apply a function of type #FuncApplyToColl to the given collection.
1461  *
1462  * @param dag The collection DAG.
1463  * @param coll The collection on which to apply the function.
1464  * @param f The function to apply to all the collections.
1465  * @param udata Additional user data parameter passed to the function.
1466  */
1467 void
1469  xmmsv_coll_t *coll,
1470  FuncApplyToColl f, void *udata)
1471 {
1472  xmms_collection_apply_to_collection_recurs (dag, coll, NULL, f, udata);
1473 }
1474 
1475 /* Internal function used for recursion (parent param, NULL by default) */
1476 static void
1477 xmms_collection_apply_to_collection_recurs (xmms_coll_dag_t *dag,
1478  xmmsv_coll_t *coll,
1479  xmmsv_coll_t *parent,
1480  FuncApplyToColl f, void *udata)
1481 {
1482  xmmsv_coll_t *op;
1483 
1484  /* Apply the function to the operator. */
1485  f (dag, coll, parent, udata);
1486 
1487  /* Recurse into the operands (if not a reference) */
1489  xmmsv_list_iter_t *iter;
1491 
1492  for (xmmsv_list_iter_first (iter);
1493  xmmsv_list_iter_valid (iter);
1494  xmmsv_list_iter_next (iter)) {
1495 
1496  xmmsv_t *val;
1497  xmmsv_list_iter_entry (iter, &val);
1498 
1499  xmmsv_get_coll (val, &op);
1500 
1501  xmms_collection_apply_to_collection_recurs (dag, op, coll, f,
1502  udata);
1503  }
1504 
1506  }
1507 }
1508 
1509 
1510 /**
1511  * Work-around function to call a function on the value of the pair.
1512  */
1513 static void
1514 call_apply_to_coll (gpointer name, gpointer coll, gpointer udata)
1515 {
1516  coll_call_infos_t *callinfos = (coll_call_infos_t*)udata;
1517 
1518  xmms_collection_apply_to_collection (callinfos->dag, coll,
1519  callinfos->func, callinfos->udata);
1520 }
1521 
1522 /**
1523  * Prepend the key string (name) to the udata list.
1524  */
1525 static void
1526 prepend_key_string (gpointer key, gpointer value, gpointer udata)
1527 {
1528  GList **list = (GList**)udata;
1529  *list = g_list_prepend (*list, xmmsv_new_string (key));
1530 }
1531 
1532 /**
1533  * Returns TRUE if the value of the pair is equal to the value stored
1534  * in the udata structure, and save the corresponding key in that
1535  * structure.
1536  */
1537 static gboolean
1538 value_match_save_key (gpointer key, gpointer val, gpointer udata)
1539 {
1540  gboolean found = FALSE;
1541  coll_table_pair_t *pair = (coll_table_pair_t*)udata;
1542  xmmsv_coll_t *coll = (xmmsv_coll_t*)val;
1543 
1544  /* value matching and key not ignored, found! */
1545  if ((coll == pair->value) &&
1546  (pair->key == NULL || strcmp (pair->key, key) != 0)) {
1547  pair->key = key;
1548  found = TRUE;
1549  }
1550 
1551  return found;
1552 }
1553 
1554 /**
1555  * If a reference, add the operator of the pointed collection as an
1556  * operand.
1557  */
1558 void
1560 {
1562  xmmsv_coll_t *target;
1563  gchar *target_name;
1564  gchar *target_namespace;
1565  gint target_nsid;
1566 
1567  xmmsv_coll_attribute_get (coll, "reference", &target_name);
1568  xmmsv_coll_attribute_get (coll, "namespace", &target_namespace);
1569  if (target_name == NULL || target_namespace == NULL ||
1570  strcmp (target_name, "All Media") == 0) {
1571  return;
1572  }
1573 
1574  target_nsid = xmms_collection_get_namespace_id (target_namespace);
1575  if (target_nsid == XMMS_COLLECTION_NSID_INVALID) {
1576  return;
1577  }
1578 
1579  target = xmms_collection_get_pointer (dag, target_name, target_nsid);
1580  if (target == NULL) {
1581  return;
1582  }
1583 
1584  xmmsv_coll_add_operand (coll, target);
1585  }
1586 }
1587 
1588 /**
1589  * If a reference, rebind the given operator to the new operator
1590  * representing the referenced collection (pointers and so are in the
1591  * udata structure).
1592  */
1593 static void
1594 rebind_references (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata)
1595 {
1597  coll_rebind_infos_t *infos;
1598 
1599  gchar *target_name = NULL;
1600  gchar *target_namespace = NULL;
1601 
1602  infos = (coll_rebind_infos_t*)udata;
1603 
1604  /* FIXME: Or only compare operand vs oldtarget ? */
1605 
1606  xmmsv_coll_attribute_get (coll, "reference", &target_name);
1607  xmmsv_coll_attribute_get (coll, "namespace", &target_namespace);
1608  if (strcmp (infos->name, target_name) != 0 ||
1609  strcmp (infos->namespace, target_namespace) != 0) {
1610  return;
1611  }
1612 
1613  xmmsv_coll_remove_operand (coll, infos->oldtarget);
1614  xmmsv_coll_add_operand (coll, infos->newtarget);
1615  }
1616 }
1617 
1618 /**
1619  * If a reference with matching name, rename it according to the
1620  * rename infos in the udata structure.
1621  */
1622 static void
1623 rename_references (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata)
1624 {
1626  coll_rename_infos_t *infos;
1627 
1628  gchar *target_name = NULL;
1629  gchar *target_namespace = NULL;
1630 
1631  infos = (coll_rename_infos_t*)udata;
1632 
1633  xmmsv_coll_attribute_get (coll, "reference", &target_name);
1634  xmmsv_coll_attribute_get (coll, "namespace", &target_namespace);
1635  if (strcmp (infos->oldname, target_name) == 0 &&
1636  strcmp (infos->namespace, target_namespace) == 0) {
1637  xmmsv_coll_attribute_set (coll, "reference", infos->newname);
1638  }
1639  }
1640 }
1641 
1642 /**
1643  * Strip reference operators to the given collection by rebinding the
1644  * parent directly to the pointed operator.
1645  */
1646 static void
1647 strip_references (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata)
1648 {
1649  xmmsv_coll_t *op;
1650  coll_rebind_infos_t *infos;
1651  gchar *target_name = NULL;
1652  gchar *target_namespace = NULL;
1653  xmmsv_list_iter_t *iter;
1654  xmmsv_t *tmp;
1655 
1656  infos = (coll_rebind_infos_t*)udata;
1657 
1659  for (xmmsv_list_iter_first (iter);
1660  xmmsv_list_iter_valid (iter);
1661  xmmsv_list_iter_next (iter)) {
1662 
1663  xmmsv_list_iter_entry (iter, &tmp);
1664  xmmsv_get_coll (tmp, &op);
1665 
1666  /* Skip if not potential reference */
1668  continue;
1669  }
1670 
1671  xmmsv_coll_attribute_get (op, "reference", &target_name);
1672  xmmsv_coll_attribute_get (op, "namespace", &target_namespace);
1673  if (strcmp (infos->name, target_name) != 0 ||
1674  strcmp (infos->namespace, target_namespace) != 0) {
1675  continue;
1676  }
1677 
1678  /* Rebind coll to ref'd operand directly, effectively strip reference */
1679  /* FIXME: Do we really need to do this _clear? */
1681 
1682  xmmsv_list_iter_remove (iter);
1683 
1684  tmp = xmmsv_new_coll (infos->oldtarget);
1685  xmmsv_list_iter_insert (iter, tmp);
1686  xmmsv_unref (tmp);
1687  }
1689 }
1690 
1691 /**
1692  * Check if the current operator is a reference to a given collection,
1693  * and if so, update the structure passed as userdata.
1694  */
1695 static void
1696 check_for_reference (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata)
1697 {
1698  coll_refcheck_t *check = (coll_refcheck_t*)udata;
1699  if (xmmsv_coll_get_type (coll) == XMMS_COLLECTION_TYPE_REFERENCE && !check->found) {
1700  gchar *target_name, *target_namespace;
1701 
1702  xmmsv_coll_attribute_get (coll, "reference", &target_name);
1703  xmmsv_coll_attribute_get (coll, "namespace", &target_namespace);
1704  if (strcmp (check->target_name, target_name) == 0 &&
1705  strcmp (check->target_namespace, target_namespace) == 0) {
1706  check->found = TRUE;
1707  } else {
1708  xmmsv_coll_t *op;
1709  xmmsv_t *tmp;
1710 
1711  if (xmmsv_list_get (xmmsv_coll_operands_get (coll), 0, &tmp)) {
1712  xmmsv_get_coll (tmp, &op);
1713  xmms_collection_apply_to_collection_recurs (dag, op, coll,
1714  check_for_reference,
1715  udata);
1716  }
1717  }
1718  }
1719 }
1720 
1721 
1722 /** Forwarding function to fix type warnings.
1723  *
1724  * @param coll The collection to unref.
1725  */
1726 static void
1727 coll_unref (void *coll)
1728 {
1729  xmmsv_coll_unref (coll);
1730 }
1731 
1732 
1733 
1734 /* ============ FIND / COLLECTION MATCH FUNCTIONS ============ */
1735 
1736 /* Generate a build_match hashtable, states initialized to UNCHECKED. */
1737 static void
1738 build_match_table (gpointer key, gpointer value, gpointer udata)
1739 {
1740  GHashTable *match_table = udata;
1741  coll_find_state_t *match = g_new (coll_find_state_t, 1);
1743  g_hash_table_replace (match_table, g_strdup (key), match);
1744 }
1745 
1746 /* Return the first unchecked element from the match_table, set the
1747  * udata pointer to contain the key of that element.
1748  */
1749 static gboolean
1750 find_unchecked (gpointer name, gpointer value, gpointer udata)
1751 {
1752  coll_find_state_t *match = value;
1753  gchar **open = udata;
1754  *open = name;
1755  return (*match == XMMS_COLLECTION_FIND_STATE_UNCHECKED);
1756 }
1757 
1758 /* Build a list of all matched entries of the match_table in the udata
1759  * pointer.
1760  */
1761 static void
1762 build_list_matches (gpointer key, gpointer value, gpointer udata)
1763 {
1764  gchar *coll_name = key;
1765  coll_find_state_t *state = value;
1766  GList **list = udata;
1767  if (*state == XMMS_COLLECTION_FIND_STATE_MATCH) {
1768  *list = g_list_prepend (*list, xmmsv_new_string (coll_name));
1769  }
1770 }
1771 
1772 /** Determine whether the mediainfos match the given collection.
1773  *
1774  * @param dag The collection DAG.
1775  * @param mediainfo The properties of the media to match against.
1776  * @param coll The collection to match with the mediainfos.
1777  * @param nsid The namespace id of the collection.
1778  * @param match_table The match_table for all collections in that namespace.
1779  * @return TRUE if the collection matches, FALSE otherwise.
1780  */
1781 static gboolean
1782 xmms_collection_media_match (xmms_coll_dag_t *dag, GHashTable *mediainfo,
1783  xmmsv_coll_t *coll, guint nsid,
1784  GHashTable *match_table)
1785 {
1786  gboolean match = FALSE;
1787  xmmsv_coll_t *op;
1788  gchar *attr1 = NULL, *attr2 = NULL;
1789  xmmsv_t *val;
1790  guint32 *idlist;
1791  gint i;
1792  gint id;
1793  xmmsv_list_iter_t *iter;
1794 
1795  switch (xmmsv_coll_get_type (coll)) {
1797  if (xmmsv_coll_attribute_get (coll, "reference", &attr1)) {
1798  if (strcmp (attr1, "All Media") == 0) {
1799  match = TRUE;
1800  } else if (xmmsv_coll_attribute_get (coll, "namespace", &attr2)) {
1801  match = xmms_collection_media_match_reference (dag, mediainfo,
1802  coll, nsid,
1803  match_table,
1804  attr1, attr2);
1805  }
1806  }
1807  break;
1808 
1810  /* if ANY matches */
1812 
1813  for (xmmsv_list_iter_first (iter);
1814  !match && xmmsv_list_iter_valid (iter);
1815  xmmsv_list_iter_next (iter)) {
1816 
1817  xmmsv_list_iter_entry (iter, &val);
1818  xmmsv_get_coll (val, &op);
1819 
1820  match = xmms_collection_media_match (dag, mediainfo, op,
1821  nsid, match_table);
1822  }
1824  break;
1825 
1827  /* if ALL match */
1828  match = TRUE;
1830 
1831  for (xmmsv_list_iter_first (iter);
1832  match && xmmsv_list_iter_valid (iter);
1833  xmmsv_list_iter_next (iter)) {
1834 
1835  xmmsv_list_iter_entry (iter, &val);
1836  xmmsv_get_coll (val, &op);
1837 
1838  match = xmms_collection_media_match (dag, mediainfo, op,
1839  nsid, match_table);
1840  }
1842  break;
1843 
1845  /* invert result from operand */
1846  match = !xmms_collection_media_match_operand (dag, mediainfo, coll,
1847  nsid, match_table);
1848  break;
1849 
1851  match = xmms_collection_media_filter_has (dag, mediainfo, coll,
1852  nsid, match_table);
1853  break;
1854 
1856  match = xmms_collection_media_filter_equals (dag, mediainfo, coll,
1857  nsid, match_table);
1858  break;
1859 
1861  match = xmms_collection_media_filter_match (dag, mediainfo, coll,
1862  nsid, match_table);
1863  break;
1864 
1866  match = xmms_collection_media_filter_smaller (dag, mediainfo, coll,
1867  nsid, match_table);
1868  break;
1869 
1871  match = xmms_collection_media_filter_greater (dag, mediainfo, coll,
1872  nsid, match_table);
1873  break;
1874 
1878  /* check if id in idlist */
1879  val = g_hash_table_lookup (mediainfo, "id");
1880  if (val != NULL) {
1881  xmmsv_get_int (val, &id);
1882  idlist = xmmsv_coll_get_idlist (coll);
1883  for (i = 0; idlist[i] != 0; i++) {
1884  /* stop if mid in the list */
1885  if (idlist[i] == id) {
1886  match = TRUE;
1887  break;
1888  }
1889  }
1890  }
1891  break;
1892 
1893  /* invalid type */
1894  default:
1895  XMMS_DBG ("invalid collection operator in xmms_collection_media_match");
1896  g_assert_not_reached ();
1897  break;
1898  }
1899 
1900  return match;
1901 }
1902 
1903 /** Determine whether the mediainfos match the given reference operator.
1904  *
1905  * @param dag The collection DAG.
1906  * @param mediainfo The properties of the media to match against.
1907  * @param coll The collection (ref op) to match with the mediainfos.
1908  * @param nsid The namespace id of the collection.
1909  * @param match_table The match_table for all collections in that namespace.
1910  * @param refname The name of the referenced collection.
1911  * @param refns The namespace of the referenced collection.
1912  * @return TRUE if the collection matches, FALSE otherwise.
1913  */
1914 static gboolean
1915 xmms_collection_media_match_reference (xmms_coll_dag_t *dag, GHashTable *mediainfo,
1916  xmmsv_coll_t *coll, guint nsid,
1917  GHashTable *match_table,
1918  const gchar *refname, const gchar *refns)
1919 {
1920  gboolean match;
1921  guint refnsid;
1922  coll_find_state_t *matchstate;
1923 
1924  /* Same NS, should be in the match table */
1925  refnsid = xmms_collection_get_namespace_id (refns);
1926  if (refnsid == nsid) {
1927  matchstate = g_hash_table_lookup (match_table, refname);
1928  if (*matchstate == XMMS_COLLECTION_FIND_STATE_UNCHECKED) {
1929  /* Check ref'd collection match status and save it */
1930  matchstate = g_new (coll_find_state_t, 1);
1931  match = xmms_collection_media_match_operand (dag,
1932  mediainfo,
1933  coll, nsid,
1934  match_table);
1935 
1936  if (match) {
1937  *matchstate = XMMS_COLLECTION_FIND_STATE_MATCH;
1938  } else {
1939  *matchstate = XMMS_COLLECTION_FIND_STATE_NOMATCH;
1940  }
1941 
1942  g_hash_table_replace (match_table, g_strdup (refname), matchstate);
1943 
1944  } else {
1945  match = (*matchstate == XMMS_COLLECTION_FIND_STATE_MATCH);
1946  }
1947 
1948  /* In another NS, just check if it matches */
1949  } else {
1950  match = xmms_collection_media_match_operand (dag, mediainfo, coll,
1951  nsid, match_table);
1952  }
1953 
1954  return match;
1955 }
1956 
1957 /** Determine whether the mediainfos match the first operand of the
1958  * given operator.
1959  *
1960  * @param dag The collection DAG.
1961  * @param mediainfo The properties of the media to match against.
1962  * @param coll Match the mediainfos with the operand of that collection.
1963  * @param nsid The namespace id of the collection.
1964  * @param match_table The match_table for all collections in that namespace.
1965  * @return TRUE if the collection matches, FALSE otherwise.
1966  */
1967 static gboolean
1968 xmms_collection_media_match_operand (xmms_coll_dag_t *dag, GHashTable *mediainfo,
1969  xmmsv_coll_t *coll, guint nsid,
1970  GHashTable *match_table)
1971 {
1972  xmmsv_coll_t *op;
1973  xmmsv_t *tmp;
1974  gboolean match = FALSE;
1975 
1976  if (xmmsv_list_get (xmmsv_coll_operands_get (coll), 0, &tmp)) {
1977  xmmsv_get_coll (tmp, &op);
1978 
1979  match = xmms_collection_media_match (dag, mediainfo, op, nsid, match_table);
1980  }
1981 
1982  return match;
1983 }
1984 
1985 /** Get all the properties for the given media.
1986  *
1987  * @param mid The id of the media.
1988  * @return A HashTable with all the properties.
1989  */
1990 static GHashTable *
1991 xmms_collection_media_info (guint mid, xmms_error_t *err)
1992 {
1993  GList *res;
1994  GList *n;
1995  GHashTable *infos;
1996  gchar *name;
1997  const gchar *buf;
1998  xmmsv_t *cmdval;
1999  xmmsv_t *value;
2000  guint state;
2001 
2002  /* FIXME: could probably reuse tree from medialib_info directly. ignores sources? */
2003  res = xmms_medialib_info_list (NULL, mid, err);
2004 
2005  /* Transform the list into a HashMap */
2006  infos = g_hash_table_new_full (g_str_hash, g_str_equal,
2007  g_free, (GDestroyNotify) xmmsv_unref);
2008  for (state = 0, n = res; n; state = (state + 1) % 3, n = n->next) {
2009  switch (state) {
2010  case 0: /* source */
2011  break;
2012 
2013  case 1: /* prop name */
2014  cmdval = n->data;
2015  xmmsv_get_string (cmdval, &buf);
2016  name = g_strdup (buf);
2017  break;
2018 
2019  case 2: /* prop value */
2020  value = xmmsv_ref (n->data);
2021 
2022  /* Only insert the first source */
2023  if (g_hash_table_lookup (infos, name) == NULL) {
2024  g_hash_table_replace (infos, name, value);
2025  }
2026  break;
2027  }
2028 
2029  xmmsv_unref (n->data);
2030  }
2031 
2032  g_list_free (res);
2033 
2034  return infos;
2035 }
2036 
2037 /** Get the string associated to the property of the mediainfo
2038  * identified by the "field" attribute of the collection.
2039  *
2040  * @return The property value as a string.
2041  */
2042 static gboolean
2043 filter_get_mediainfo_field_string (xmmsv_coll_t *coll,
2044  GHashTable *mediainfo, gchar **val)
2045 {
2046  gboolean retval = FALSE;
2047  gchar *attr;
2048  xmmsv_t *cmdval;
2049 
2050  if (xmmsv_coll_attribute_get (coll, "field", &attr)) {
2051  cmdval = g_hash_table_lookup (mediainfo, attr);
2052  if (cmdval != NULL) {
2053  switch (xmmsv_get_type (cmdval)) {
2054  case XMMSV_TYPE_STRING:
2055  {
2056  const gchar *s;
2057  xmmsv_get_string (cmdval, &s);
2058  *val = g_strdup (s);
2059  retval = TRUE;
2060  break;
2061  }
2062  case XMMSV_TYPE_INT32:
2063  {
2064  gint i;
2065  xmmsv_get_int (cmdval, &i);
2066  *val = g_strdup_printf ("%d", i);
2067  retval = TRUE;
2068  break;
2069  }
2070  default:
2071  break;
2072  }
2073  }
2074  }
2075 
2076  return retval;
2077 }
2078 
2079 /** Get the integer associated to the property of the mediainfo
2080  * identified by the "field" attribute of the collection.
2081  *
2082  * @return The property value as an integer.
2083  */
2084 static gboolean
2085 filter_get_mediainfo_field_int (xmmsv_coll_t *coll, GHashTable *mediainfo, gint *val)
2086 {
2087  gboolean retval = FALSE;
2088  gchar *attr;
2089  xmmsv_t *cmdval;
2090 
2091  if (xmmsv_coll_attribute_get (coll, "field", &attr)) {
2092  cmdval = g_hash_table_lookup (mediainfo, attr);
2093  if (cmdval != NULL && xmmsv_get_type (cmdval) == XMMSV_TYPE_INT32) {
2094  xmmsv_get_int (cmdval, val);
2095  retval = TRUE;
2096  }
2097  }
2098 
2099  return retval;
2100 }
2101 
2102 /* Get the string value of the "value" attribute of the collection. */
2103 static gboolean
2104 filter_get_operator_value_string (xmmsv_coll_t *coll, const gchar **val)
2105 {
2106  gchar *attr;
2107  gboolean valid;
2108 
2109  valid = xmmsv_coll_attribute_get (coll, "value", &attr);
2110  if (valid) {
2111  *val = attr;
2112  }
2113 
2114  return valid;
2115 }
2116 
2117 /* Get the integer value of the "value" attribute of the collection. */
2118 static gboolean
2119 filter_get_operator_value_int (xmmsv_coll_t *coll, gint *val)
2120 {
2121  gint buf;
2122  gboolean valid;
2123 
2124  valid = xmms_collection_get_int_attr (coll, "value", &buf);
2125  if (valid) {
2126  *val = buf;
2127  }
2128 
2129  return valid;
2130 }
2131 
2132 /* Check whether the given operator has the "case-sensitive" attribute
2133  * or not. */
2134 static gboolean
2135 filter_get_operator_case (xmmsv_coll_t *coll, gboolean *val)
2136 {
2137  gchar *attr;
2138 
2139  if (xmmsv_coll_attribute_get (coll, "case-sensitive", &attr)) {
2140  *val = (strcmp (attr, "true") == 0);
2141  }
2142  else {
2143  *val = FALSE;
2144  }
2145 
2146  return TRUE;
2147 }
2148 
2149 /* Check whether the HAS filter operator matches the mediainfo. */
2150 static gboolean
2151 xmms_collection_media_filter_has (xmms_coll_dag_t *dag, GHashTable *mediainfo,
2152  xmmsv_coll_t *coll, guint nsid,
2153  GHashTable *match_table)
2154 {
2155  gboolean match = FALSE;
2156  gchar *mediaval;
2157 
2158  /* If operator matches, recurse upwards in the operand */
2159  if (filter_get_mediainfo_field_string (coll, mediainfo, &mediaval)) {
2160  match = xmms_collection_media_match_operand (dag, mediainfo, coll,
2161  nsid, match_table);
2162 
2163  g_free (mediaval);
2164  }
2165 
2166  return match;
2167 }
2168 
2169 /* Check whether the MATCH filter operator matches the mediainfo. */
2170 static gboolean
2171 xmms_collection_media_filter_equals (xmms_coll_dag_t *dag, GHashTable *mediainfo,
2172  xmmsv_coll_t *coll, guint nsid,
2173  GHashTable *match_table)
2174 {
2175  gboolean match = FALSE;
2176  gchar *mediaval = NULL;
2177  const gchar *opval;
2178  gboolean case_sens;
2179 
2180  if (filter_get_mediainfo_field_string (coll, mediainfo, &mediaval) &&
2181  filter_get_operator_value_string (coll, &opval) &&
2182  filter_get_operator_case (coll, &case_sens)) {
2183 
2184  if (case_sens) {
2185  match = (strcmp (mediaval, opval) == 0);
2186  } else {
2187  match = (g_ascii_strcasecmp (mediaval, opval) == 0);
2188  }
2189  }
2190 
2191  /* If operator matches, recurse upwards in the operand */
2192  if (match) {
2193  match = xmms_collection_media_match_operand (dag, mediainfo, coll,
2194  nsid, match_table);
2195  }
2196 
2197  if (mediaval != NULL) {
2198  g_free (mediaval);
2199  }
2200 
2201  return match;
2202 }
2203 
2204 /* Check whether the MATCH filter operator matches the mediainfo. */
2205 static gboolean
2206 xmms_collection_media_filter_match (xmms_coll_dag_t *dag, GHashTable *mediainfo,
2207  xmmsv_coll_t *coll, guint nsid,
2208  GHashTable *match_table)
2209 {
2210  gboolean match = FALSE;
2211  gchar *buf, *opval, *mediaval;
2212  const gchar *s;
2213  gboolean case_sens;
2214 
2215  if (filter_get_mediainfo_field_string (coll, mediainfo, &buf) &&
2216  filter_get_operator_value_string (coll, &s) &&
2217  filter_get_operator_case (coll, &case_sens)) {
2218 
2219  /* Prepare values */
2220  if (case_sens) {
2221  opval = g_strdup (s);
2222  mediaval = g_strdup (buf);
2223  } else {
2224  opval = g_utf8_strdown (s, -1);
2225  mediaval = g_utf8_strdown (buf, -1);
2226  }
2227 
2228  match = g_pattern_match_simple (opval, mediaval);
2229 
2230  g_free (buf);
2231  g_free (opval);
2232  g_free (mediaval);
2233 
2234  /* If operator matches, recurse upwards in the operand */
2235  if (match) {
2236  match = xmms_collection_media_match_operand (dag, mediainfo, coll,
2237  nsid, match_table);
2238  }
2239  }
2240 
2241  return match;
2242 }
2243 
2244 /* Check whether the SMALLER filter operator matches the mediainfo. */
2245 static gboolean
2246 xmms_collection_media_filter_smaller (xmms_coll_dag_t *dag, GHashTable *mediainfo,
2247  xmmsv_coll_t *coll, guint nsid,
2248  GHashTable *match_table)
2249 {
2250  gboolean match = FALSE;
2251  gint mediaval;
2252  gint opval;
2253 
2254  /* If operator matches, recurse upwards in the operand */
2255  if (filter_get_mediainfo_field_int (coll, mediainfo, &mediaval) &&
2256  filter_get_operator_value_int (coll, &opval) &&
2257  (mediaval < opval) ) {
2258 
2259  match = xmms_collection_media_match_operand (dag, mediainfo, coll,
2260  nsid, match_table);
2261  }
2262 
2263  return match;
2264 }
2265 
2266 /* Check whether the GREATER filter operator matches the mediainfo. */
2267 static gboolean
2268 xmms_collection_media_filter_greater (xmms_coll_dag_t *dag, GHashTable *mediainfo,
2269  xmmsv_coll_t *coll, guint nsid,
2270  GHashTable *match_table)
2271 {
2272  gboolean match = FALSE;
2273  gint mediaval;
2274  gint opval;
2275 
2276  /* If operator matches, recurse upwards in the operand */
2277  if (filter_get_mediainfo_field_int (coll, mediainfo, &mediaval) &&
2278  filter_get_operator_value_int (coll, &opval) &&
2279  (mediaval > opval) ) {
2280 
2281  match = xmms_collection_media_match_operand (dag, mediainfo, coll,
2282  nsid, match_table);
2283  }
2284 
2285  return match;
2286 }
struct xmmsv_St xmmsv_t
Definition: xmmsv.h:51
#define XMMS_CMD_FUNC(cmdid)
Definition: xmms_object.h:181
G_BEGIN_DECLS typedef gint32 xmms_medialib_entry_t
Definition: xmms_medialib.h:86
int xmmsv_coll_idlist_append(xmmsv_coll_t *coll, unsigned int id)
Append a value to the idlist.
Definition: coll.c:275
coll_find_state_t
Definition: collection.c:73
#define XMMS_OBJECT(p)
Definition: xmms_object.h:84
xmms_medialib_entry_t xmms_collection_get_random_media(xmms_coll_dag_t *dag, xmmsv_coll_t *source)
Get a random media entry from the given collection.
Definition: collection.c:1032
const gchar * xmms_collection_get_namespace_string(xmms_collection_namespace_id_t nsid)
Find the namespace name (string) corresponding to a namespace id.
Definition: collection.c:1376
void xmmsv_unref(xmmsv_t *val)
Decreases the references for the xmmsv_t When the number of references reaches 0 it will be freed...
Definition: value.c:301
#define xmms_object_unref(obj)
Definition: xmms_object.h:193
int xmmsv_coll_attribute_get(xmmsv_coll_t *coll, const char *key, char **value)
Retrieve the value of the attribute of the given collection.
Definition: coll.c:550
void bind_all_references(xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata)
If a reference, add the operator of the pointed collection as an operand.
Definition: collection.c:1559
xmms_collection_namespace_id_t xmms_collection_get_namespace_id(const gchar *namespace)
Find the namespace id corresponding to a namespace string.
Definition: collection.c:1353
int xmmsv_list_get(xmmsv_t *listv, int pos, xmmsv_t **val)
Get the element at the given position in the list xmmsv_t.
Definition: value.c:1210
void(* FuncApplyToColl)(xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata)
int xmmsv_get_list_iter(const xmmsv_t *val, xmmsv_list_iter_t **it)
Retrieves a list iterator from a list xmmsv_t.
Definition: value.c:918
#define xmms_error_iserror(e)
Definition: xmms_error.h:57
struct xmms_stream_type_St xmms_stream_type_t
int xmmsv_dict_remove(xmmsv_t *dictv, const char *key)
Remove the element corresponding to a given key in the dict xmmsv_t (if it exists).
Definition: value.c:1795
int xmmsv_list_iter_insert(xmmsv_list_iter_t *it, xmmsv_t *val)
Insert an element in the list at the position pointed at by the iterator.
Definition: value.c:1628
void xmms_object_cmd_add(xmms_object_t *object, guint cmdid, const xmms_object_cmd_desc_t *desc)
Add a command that could be called from the client API to a object.
Definition: object.c:321
int xmmsv_list_iter_entry(xmmsv_list_iter_t *it, xmmsv_t **val)
Get the element currently pointed at by the iterator.
Definition: value.c:1487
void xmms_ipc_broadcast_register(xmms_object_t *object, xmms_ipc_signals_t signalid)
Register a broadcast signal.
Definition: ipc.c:694
void xmmsv_coll_attribute_set(xmmsv_coll_t *coll, const char *key, const char *value)
Set an attribute in the given collection.
Definition: coll.c:512
xmmsv_t * xmmsv_new_string(const char *s)
Allocates a new string xmmsv_t.
Definition: value.c:178
struct xmms_xform_St xmms_xform_t
xmmsv_coll_t * xmmsv_coll_ref(xmmsv_coll_t *coll)
Increases the references for the xmmsv_coll_t.
Definition: coll.c:66
void xmmsv_list_iter_first(xmmsv_list_iter_t *it)
Rewind the iterator to the start of the list.
Definition: value.c:1515
int xmmsv_get_int(const xmmsv_t *val, int32_t *r)
Retrieves a signed integer from the value.
Definition: value.c:815
void xmms_coll_sync_init(xmms_coll_dag_t *dag)
Get the collection-to-database-synchronization thread running.
Definition: collsync.c:81
xmmsv_coll_t * xmms_collection_get_pointer(xmms_coll_dag_t *dag, const gchar *collname, guint nsid)
Find the collection structure corresponding to the given name in the given namespace.
Definition: collection.c:927
#define XMMS_MAX_INT_ATTRIBUTE_LEN
Definition: xmms_playlist.h:36
int xmmsv_list_append(xmmsv_t *listv, xmmsv_t *val)
Append an element to the end of the list xmmsv_t.
Definition: value.c:1332
gboolean check_string_list(xmmsv_t *list)
Checks that the list only contains string values.
Definition: object.c:470
void xmms_collection_changed_msg_send(xmms_coll_dag_t *colldag, GTree *dict)
Definition: collection.c:175
#define XMMS_CMD_DEFINE4(cmdid, realfunc, argtype0, _rettype, argtype1, argtype2, argtype3, argtype4)
Definition: xmms_object.h:178
gboolean xmms_medialib_entry_property_set_int_source(xmms_medialib_session_t *session, xmms_medialib_entry_t entry, const gchar *property, gint value, guint32 source)
Definition: medialib.c:627
xmmsv_coll_type_t
guint32 xmms_medialib_source_to_id(xmms_medialib_session_t *session, const gchar *source)
Definition: medialib.c:273
GList * xmms_medialib_select(xmms_medialib_session_t *session, const gchar *query, xmms_error_t *error)
Get a list of GHashTables &#39;s that matches the query.
Definition: medialib.c:1422
xmms_xform_t * xmms_xform_chain_setup_url(xmms_medialib_entry_t entry, const gchar *url, GList *goal_formats, gboolean rehash)
Definition: xform.c:1517
GList * xmms_xform_browse_method(xmms_xform_t *xform, const gchar *url, xmms_error_t *error)
Definition: xform.c:259
struct xmmsv_St * xmmsv_coll_operands_get(xmmsv_coll_t *coll)
Definition: coll.c:489
gboolean xmms_collection_get_int_attr(xmmsv_coll_t *coll, const gchar *attrname, gint *val)
Extract an attribute from a collection as an integer.
Definition: collection.c:952
int xmmsv_dict_get(xmmsv_t *dictv, const char *key, xmmsv_t **val)
Get the element corresponding to the given key in the dict xmmsv_t (if it exists).
Definition: value.c:1709
int xmmsv_dict_foreach(xmmsv_t *dictv, xmmsv_dict_foreach_func func, void *user_data)
Apply a function to each key-element pair in the list.
Definition: value.c:1845
GList * xmms_collection_query_ids(xmms_coll_dag_t *dag, xmmsv_coll_t *coll, gint32 lim_start, gint32 lim_len, xmmsv_t *order, xmms_error_t *err)
Find the ids of the media matched by a collection.
Definition: collection.c:785
xmms_collection_namespace_id_t
#define xmms_log_error(fmt,...)
Definition: xmms_log.h:35
#define XMMS_COLLECTION_CHANGED_MSG(type, name, namespace)
Definition: collection.c:188
void xmmsv_list_iter_explicit_destroy(xmmsv_list_iter_t *it)
Explicitly free list iterator.
Definition: value.c:1470
int xmmsv_get_coll(const xmmsv_t *val, xmmsv_coll_t **coll)
Retrieves a collection from the value.
Definition: value.c:875
#define XMMS_COLLECTION_NS_PLAYLISTS
GList * xmms_medialib_info_list(xmms_medialib_t *medialib, guint32 id, xmms_error_t *err)
Definition: medialib.c:1226
xmmsv_t * xmmsv_new_coll(xmmsv_coll_t *coll)
Allocates a new collection xmmsv_t.
Definition: value.c:200
void xmms_object_emit_f(xmms_object_t *object, guint32 signalid, xmmsv_type_t type,...)
Emits a signal on the current object.
Definition: object.c:257
xmms_medialib_entry_t xmms_medialib_entry_new_encoded(xmms_medialib_session_t *session, const char *url, xmms_error_t *error)
Definition: medialib.c:996
struct xmmsv_coll_St xmmsv_coll_t
Definition: xmmsv_coll.h:28
int xmmsv_list_get_size(xmmsv_t *listv)
Return the size of the list.
Definition: value.c:1395
GString * xmms_collection_get_query(xmms_coll_dag_t *dag, xmmsv_coll_t *coll, guint limit_start, guint limit_len, xmmsv_t *order, xmmsv_t *fetch, xmmsv_t *group)
Definition: collquery.c:97
struct xmms_playlist_St xmms_playlist_t
Definition: xmms_playlist.h:41
void xmms_collection_apply_to_all_collections(xmms_coll_dag_t *dag, FuncApplyToColl f, void *udata)
Apply a function of type FuncApplyToColl to all the collections in all namespaces.
Definition: collection.c:1449
int xmmsv_list_iter_remove(xmmsv_list_iter_t *it)
Remove the element in the list at the position pointed at by the iterator.
Definition: value.c:1644
void xmmsv_list_iter_next(xmmsv_list_iter_t *it)
Advance the iterator to the next element in the list.
Definition: value.c:1545
void xmms_collection_dag_save(xmms_coll_dag_t *dag)
Save the collection DAG in the database.
Definition: collserial.c:53
#define XMMS_CMD_DEFINE3(cmdid, realfunc, argtype0, _rettype, argtype1, argtype2, argtype3)
Definition: xmms_object.h:177
struct add_metadata_from_tree_user_data_St add_metadata_from_tree_user_data_t
void xmms_collection_update_pointer(xmms_coll_dag_t *dag, const gchar *name, guint nsid, xmmsv_coll_t *newtarget)
Update a reference to point to a new collection.
Definition: collection.c:903
xmmsv_coll_t * xmmsv_coll_new(xmmsv_coll_type_t type)
Allocate a new collection of the given type.
Definition: coll.c:83
int xmmsv_list_clear(xmmsv_t *listv)
Empty the list from all its elements.
Definition: value.c:1348
xmmsv_type_t xmmsv_get_type(const xmmsv_t *val)
Get the type of the value.
Definition: value.c:384
xmms_collection_changed_actions_t
xmmsv_t * xmmsv_new_int(int32_t i)
Allocates a new integer xmmsv_t.
Definition: value.c:159
#define XMMS_CMD_DEFINE(cmdid, realfunc, argtype0, _rettype, argtype1, argtype2)
Definition: xmms_object.h:176
void xmms_ipc_object_unregister(xmms_ipc_objects_t objectid)
Remove a object from the IPC core.
Definition: ipc.c:769
gboolean xmms_collection_set_int_attr(xmmsv_coll_t *coll, const gchar *attrname, gint newval)
Set the attribute of a collection as an integer.
Definition: collection.c:980
void xmmsv_coll_add_operand(xmmsv_coll_t *coll, xmmsv_coll_t *op)
Add the operand to the given collection.
Definition: coll.c:218
xmmsv_t * xmmsv_ref(xmmsv_t *val)
References the xmmsv_t.
Definition: value.c:286
void xmms_collection_dag_replace(xmms_coll_dag_t *dag, xmms_collection_namespace_id_t nsid, gchar *key, xmmsv_coll_t *newcoll)
Update the DAG to update the value of the pair with the given key.
Definition: collection.c:912
xmms_stream_type_t * _xmms_stream_type_new(void *dumb,...)
Definition: streamtype.c:362
#define XMMS_ACTIVE_PLAYLIST
const gchar * xmms_collection_find_alias(xmms_coll_dag_t *dag, guint nsid, xmmsv_coll_t *value, const gchar *key)
Reverse-search the list of collections in the given namespace to find the first pair whose value matc...
Definition: collection.c:1009
void xmmsv_coll_remove_operand(xmmsv_coll_t *coll, xmmsv_coll_t *op)
Remove all the occurences of the operand in the given collection.
Definition: coll.c:249
struct xmms_medialib_session_St xmms_medialib_session_t
Definition: xmms_medialib.h:87
void xmms_coll_sync_shutdown()
Shutdown the collection-to-database-synchronization thread.
Definition: collsync.c:93
#define XMMS_PLAYLIST_COLLECTION_CHANGED_MSG(playlist, name)
Definition: xmms_playlist.h:68
void xmms_collection_apply_to_collection(xmms_coll_dag_t *dag, xmmsv_coll_t *coll, FuncApplyToColl f, void *udata)
Apply a function of type FuncApplyToColl to the given collection.
Definition: collection.c:1468
#define XMMS_COLLECTION_NUM_NAMESPACES
void xmms_collection_dag_restore(xmms_coll_dag_t *dag)
Restore the collection DAG from the database.
Definition: collserial.c:85
void xmms_coll_sync_schedule_sync()
Schedule a collection-to-database-synchronization in 10 seconds.
Definition: collsync.c:110
xmmsv_t * xmmsv_new_list(void)
Allocates a new list xmmsv_t.
Definition: value.c:248
#define XMMS_DBG(fmt,...)
Definition: xmms_log.h:32
#define xmms_medialib_begin_write()
#define xmms_medialib_begin()
XMMS_CMD_DEFINE6(query_infos, xmms_collection_client_query_infos, xmms_coll_dag_t *, LIST, COLL, INT32, INT32, LIST, LIST, LIST)
void xmms_object_connect(xmms_object_t *object, guint32 signalid, xmms_object_handler_t handler, gpointer userdata)
Connect to a signal that is emitted by this object.
Definition: object.c:116
void xmms_ipc_broadcast_unregister(xmms_ipc_signals_t signalid)
Unregister a broadcast signal.
Definition: ipc.c:709
xmms_coll_dag_t * xmms_collection_init(xmms_playlist_t *playlist)
Initializes a new xmms_coll_dag_t.
Definition: collection.c:226
gboolean xmms_medialib_entry_property_set_str_source(xmms_medialib_session_t *session, xmms_medialib_entry_t entry, const gchar *property, const gchar *value, guint32 source)
Definition: medialib.c:677
int xmmsv_get_string(const xmmsv_t *val, const char **r)
Retrieves a string from the value.
Definition: value.c:855
void xmms_collection_foreach_in_namespace(xmms_coll_dag_t *dag, guint nsid, GHFunc f, void *udata)
Apply a function to all the collections in a given namespace.
Definition: collection.c:1429
#define xmms_object_new(objtype, destroyfunc)
Definition: xmms_object.h:199
G_BEGIN_DECLS struct xmms_error_St xmms_error_t
uint32_t * xmmsv_coll_get_idlist(xmmsv_coll_t *coll)
Return the list of ids stored in the collection.
Definition: coll.c:481
void xmms_ipc_object_register(xmms_ipc_objects_t objectid, xmms_object_t *object)
Register a object to the IPC core.
Definition: ipc.c:758
#define XMMS_COLLECTION_NS_COLLECTIONS
void xmms_collection_sync(xmms_coll_dag_t *dag)
Synchronize collection data to the database (i.e.
Definition: collection.c:593
xmmsv_coll_type_t xmmsv_coll_get_type(xmmsv_coll_t *coll)
Return the type of the collection.
Definition: coll.c:464
void xmms_medialib_end(xmms_medialib_session_t *session)
Definition: medialib.c:470
#define XMMS_COLLECTION_NS_ALL
GTree * xmms_collection_changed_msg_new(xmms_collection_changed_actions_t type, const gchar *plname, const gchar *namespace)
Definition: collection.c:159
struct xmmsv_list_iter_St xmmsv_list_iter_t
Definition: xmmsv.h:53
int xmmsv_list_iter_valid(xmmsv_list_iter_t *it)
Check whether the iterator is valid and points to a valid element.
Definition: value.c:1504
struct xmms_coll_dag_St xmms_coll_dag_t
void xmmsv_coll_unref(xmmsv_coll_t *coll)
Decreases the references for the xmmsv_coll_t When the number of references reaches 0 it will be free...
Definition: coll.c:147