00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include <glib.h>
00030 #include <stdio.h>
00031 #include <string.h>
00032
00033 #include <libaudcore/audstrings.h>
00034
00035 #include "debug.h"
00036 #include "interface.h"
00037 #include "misc.h"
00038 #include "plugin.h"
00039 #include "plugins.h"
00040 #include "util.h"
00041
00042 #define FILENAME "plugin-registry"
00043 #define FORMAT 6
00044
00045 typedef struct {
00046 GList * schemes;
00047 } TransportPluginData;
00048
00049 typedef struct {
00050 GList * exts;
00051 } PlaylistPluginData;
00052
00053 typedef struct {
00054 GList * keys[INPUT_KEYS];
00055 gboolean has_images, has_subtunes, can_write_tuple, has_infowin;
00056 } InputPluginData;
00057
00058 struct PluginHandle {
00059 gchar * path;
00060 gboolean confirmed, loaded;
00061 gint timestamp, type;
00062 Plugin * header;
00063 gchar * name;
00064 gint priority;
00065 gboolean has_about, has_configure, enabled;
00066 GList * watches;
00067
00068 union {
00069 TransportPluginData t;
00070 PlaylistPluginData p;
00071 InputPluginData i;
00072 } u;
00073 };
00074
00075 typedef struct {
00076 PluginForEachFunc func;
00077 void * data;
00078 } PluginWatch;
00079
00080 static const gchar * plugin_type_names[] = {
00081 [PLUGIN_TYPE_TRANSPORT] = "transport",
00082 [PLUGIN_TYPE_PLAYLIST] = "playlist",
00083 [PLUGIN_TYPE_INPUT] = "input",
00084 [PLUGIN_TYPE_EFFECT] = "effect",
00085 [PLUGIN_TYPE_OUTPUT] = "output",
00086 [PLUGIN_TYPE_VIS] = "vis",
00087 [PLUGIN_TYPE_GENERAL] = "general",
00088 [PLUGIN_TYPE_IFACE] = "iface"};
00089
00090 static const gchar * input_key_names[] = {
00091 [INPUT_KEY_SCHEME] = "scheme",
00092 [INPUT_KEY_EXTENSION] = "ext",
00093 [INPUT_KEY_MIME] = "mime"};
00094
00095 static GList * plugin_list = NULL;
00096 static gboolean registry_locked = TRUE;
00097 static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
00098
00099 static PluginHandle * plugin_new (gchar * path, gboolean confirmed, gboolean
00100 loaded, gint timestamp, gint type, Plugin * header)
00101 {
00102 PluginHandle * plugin = g_malloc (sizeof (PluginHandle));
00103
00104 plugin->path = path;
00105 plugin->confirmed = confirmed;
00106 plugin->loaded = loaded;
00107 plugin->timestamp = timestamp;
00108 plugin->type = type;
00109 plugin->header = header;
00110 plugin->name = NULL;
00111 plugin->priority = 0;
00112 plugin->has_about = FALSE;
00113 plugin->has_configure = FALSE;
00114 plugin->enabled = FALSE;
00115 plugin->watches = NULL;
00116
00117 if (type == PLUGIN_TYPE_TRANSPORT)
00118 {
00119 plugin->enabled = TRUE;
00120 plugin->u.t.schemes = NULL;
00121 }
00122 else if (type == PLUGIN_TYPE_PLAYLIST)
00123 {
00124 plugin->enabled = TRUE;
00125 plugin->u.p.exts = NULL;
00126 }
00127 else if (type == PLUGIN_TYPE_INPUT)
00128 {
00129 plugin->enabled = TRUE;
00130 memset (plugin->u.i.keys, 0, sizeof plugin->u.i.keys);
00131 plugin->u.i.has_images = FALSE;
00132 plugin->u.i.has_subtunes = FALSE;
00133 plugin->u.i.can_write_tuple = FALSE;
00134 plugin->u.i.has_infowin = FALSE;
00135 }
00136
00137 plugin_list = g_list_prepend (plugin_list, plugin);
00138 return plugin;
00139 }
00140
00141 static void plugin_free (PluginHandle * plugin)
00142 {
00143 plugin_list = g_list_remove (plugin_list, plugin);
00144
00145 g_list_foreach (plugin->watches, (GFunc) g_free, NULL);
00146 g_list_free (plugin->watches);
00147
00148 if (plugin->type == PLUGIN_TYPE_TRANSPORT)
00149 {
00150 g_list_foreach (plugin->u.t.schemes, (GFunc) g_free, NULL);
00151 g_list_free (plugin->u.t.schemes);
00152 }
00153 else if (plugin->type == PLUGIN_TYPE_PLAYLIST)
00154 {
00155 g_list_foreach (plugin->u.p.exts, (GFunc) g_free, NULL);
00156 g_list_free (plugin->u.p.exts);
00157 }
00158 else if (plugin->type == PLUGIN_TYPE_INPUT)
00159 {
00160 for (gint key = 0; key < INPUT_KEYS; key ++)
00161 {
00162 g_list_foreach (plugin->u.i.keys[key], (GFunc) g_free, NULL);
00163 g_list_free (plugin->u.i.keys[key]);
00164 }
00165 }
00166
00167 g_free (plugin->path);
00168 g_free (plugin->name);
00169 g_free (plugin);
00170 }
00171
00172 static FILE * open_registry_file (const gchar * mode)
00173 {
00174 gchar * path = g_strdup_printf ("%s/" FILENAME, get_path (AUD_PATH_USER_DIR));
00175 FILE * file = fopen (path, mode);
00176 g_free (path);
00177 return file;
00178 }
00179
00180 static void transport_plugin_save (PluginHandle * plugin, FILE * handle)
00181 {
00182 for (GList * node = plugin->u.t.schemes; node; node = node->next)
00183 fprintf (handle, "scheme %s\n", (const gchar *) node->data);
00184 }
00185
00186 static void playlist_plugin_save (PluginHandle * plugin, FILE * handle)
00187 {
00188 for (GList * node = plugin->u.p.exts; node; node = node->next)
00189 fprintf (handle, "ext %s\n", (const gchar *) node->data);
00190 }
00191
00192 static void input_plugin_save (PluginHandle * plugin, FILE * handle)
00193 {
00194 for (gint key = 0; key < INPUT_KEYS; key ++)
00195 {
00196 for (GList * node = plugin->u.i.keys[key]; node; node = node->next)
00197 fprintf (handle, "%s %s\n", input_key_names[key], (const gchar *)
00198 node->data);
00199 }
00200
00201 fprintf (handle, "images %d\n", plugin->u.i.has_images);
00202 fprintf (handle, "subtunes %d\n", plugin->u.i.has_subtunes);
00203 fprintf (handle, "writes %d\n", plugin->u.i.can_write_tuple);
00204 fprintf (handle, "infowin %d\n", plugin->u.i.has_infowin);
00205 }
00206
00207 static void plugin_save (PluginHandle * plugin, FILE * handle)
00208 {
00209 fprintf (handle, "%s %s\n", plugin_type_names[plugin->type], plugin->path);
00210 fprintf (handle, "stamp %d\n", plugin->timestamp);
00211 fprintf (handle, "name %s\n", plugin->name);
00212 fprintf (handle, "priority %d\n", plugin->priority);
00213 fprintf (handle, "about %d\n", plugin->has_about);
00214 fprintf (handle, "config %d\n", plugin->has_configure);
00215 fprintf (handle, "enabled %d\n", plugin->enabled);
00216
00217 if (plugin->type == PLUGIN_TYPE_TRANSPORT)
00218 transport_plugin_save (plugin, handle);
00219 else if (plugin->type == PLUGIN_TYPE_PLAYLIST)
00220 playlist_plugin_save (plugin, handle);
00221 else if (plugin->type == PLUGIN_TYPE_INPUT)
00222 input_plugin_save (plugin, handle);
00223 }
00224
00225 void plugin_registry_save (void)
00226 {
00227 FILE * handle = open_registry_file ("w");
00228 g_return_if_fail (handle);
00229
00230 fprintf (handle, "format %d\n", FORMAT);
00231
00232 g_list_foreach (plugin_list, (GFunc) plugin_save, handle);
00233 fclose (handle);
00234
00235 g_list_foreach (plugin_list, (GFunc) plugin_free, NULL);
00236 registry_locked = TRUE;
00237 }
00238
00239 static gchar parse_key[512];
00240 static gchar * parse_value;
00241
00242 static void parse_next (FILE * handle)
00243 {
00244 parse_value = NULL;
00245
00246 if (! fgets (parse_key, sizeof parse_key, handle))
00247 return;
00248
00249 gchar * space = strchr (parse_key, ' ');
00250 if (! space)
00251 return;
00252
00253 * space = 0;
00254 parse_value = space + 1;
00255
00256 gchar * newline = strchr (parse_value, '\n');
00257 if (newline)
00258 * newline = 0;
00259 }
00260
00261 static gboolean parse_integer (const gchar * key, gint * value)
00262 {
00263 return (parse_value && ! strcmp (parse_key, key) && sscanf (parse_value,
00264 "%d", value) == 1);
00265 }
00266
00267 static gchar * parse_string (const gchar * key)
00268 {
00269 return (parse_value && ! strcmp (parse_key, key)) ? g_strdup (parse_value) :
00270 NULL;
00271 }
00272
00273 static void transport_plugin_parse (PluginHandle * plugin, FILE * handle)
00274 {
00275 gchar * value;
00276 while ((value = parse_string ("scheme")))
00277 {
00278 plugin->u.t.schemes = g_list_prepend (plugin->u.t.schemes, value);
00279 parse_next (handle);
00280 }
00281 }
00282
00283 static void playlist_plugin_parse (PluginHandle * plugin, FILE * handle)
00284 {
00285 gchar * value;
00286 while ((value = parse_string ("ext")))
00287 {
00288 plugin->u.p.exts = g_list_prepend (plugin->u.p.exts, value);
00289 parse_next (handle);
00290 }
00291 }
00292
00293 static void input_plugin_parse (PluginHandle * plugin, FILE * handle)
00294 {
00295 for (gint key = 0; key < INPUT_KEYS; key ++)
00296 {
00297 gchar * value;
00298 while ((value = parse_string (input_key_names[key])))
00299 {
00300 plugin->u.i.keys[key] = g_list_prepend (plugin->u.i.keys[key],
00301 value);
00302 parse_next (handle);
00303 }
00304 }
00305
00306 if (parse_integer ("images", & plugin->u.i.has_images))
00307 parse_next (handle);
00308 if (parse_integer ("subtunes", & plugin->u.i.has_subtunes))
00309 parse_next (handle);
00310 if (parse_integer ("writes", & plugin->u.i.can_write_tuple))
00311 parse_next (handle);
00312 if (parse_integer ("infowin", & plugin->u.i.has_infowin))
00313 parse_next (handle);
00314 }
00315
00316 static gboolean plugin_parse (FILE * handle)
00317 {
00318 gchar * path = NULL;
00319
00320 gint type;
00321 for (type = 0; type < PLUGIN_TYPES; type ++)
00322 {
00323 if ((path = parse_string (plugin_type_names[type])))
00324 goto FOUND;
00325 }
00326
00327 return FALSE;
00328
00329 FOUND:
00330 parse_next (handle);
00331
00332 gint timestamp;
00333 if (! parse_integer ("stamp", & timestamp))
00334 {
00335 g_free (path);
00336 return FALSE;
00337 }
00338
00339 PluginHandle * plugin = plugin_new (path, FALSE, FALSE, timestamp, type,
00340 NULL);
00341 parse_next (handle);
00342
00343 if ((plugin->name = parse_string ("name")))
00344 parse_next (handle);
00345 if (parse_integer ("priority", & plugin->priority))
00346 parse_next (handle);
00347 if (parse_integer ("about", & plugin->has_about))
00348 parse_next (handle);
00349 if (parse_integer ("config", & plugin->has_configure))
00350 parse_next (handle);
00351 if (parse_integer ("enabled", & plugin->enabled))
00352 parse_next (handle);
00353
00354 if (type == PLUGIN_TYPE_TRANSPORT)
00355 transport_plugin_parse (plugin, handle);
00356 else if (type == PLUGIN_TYPE_PLAYLIST)
00357 playlist_plugin_parse (plugin, handle);
00358 else if (type == PLUGIN_TYPE_INPUT)
00359 input_plugin_parse (plugin, handle);
00360
00361 return TRUE;
00362 }
00363
00364 void plugin_registry_load (void)
00365 {
00366 FILE * handle = open_registry_file ("r");
00367 if (! handle)
00368 goto UNLOCK;
00369
00370 parse_next (handle);
00371
00372 gint format;
00373 if (! parse_integer ("format", & format) || format != FORMAT)
00374 goto ERR;
00375
00376 parse_next (handle);
00377
00378 while (plugin_parse (handle))
00379 ;
00380
00381 ERR:
00382 fclose (handle);
00383 UNLOCK:
00384 registry_locked = FALSE;
00385 }
00386
00387 static void plugin_prune (PluginHandle * plugin)
00388 {
00389 if (plugin->confirmed)
00390 return;
00391
00392 AUDDBG ("Plugin not found: %s\n", plugin->path);
00393 plugin_free (plugin);
00394 }
00395
00396 gint plugin_compare (PluginHandle * a, PluginHandle * b)
00397 {
00398 if (a->type < b->type)
00399 return -1;
00400 if (a->type > b->type)
00401 return 1;
00402 if (a->priority < b->priority)
00403 return -1;
00404 if (a->priority > b->priority)
00405 return 1;
00406
00407 gint diff;
00408 if ((diff = string_compare (a->name, b->name)))
00409 return diff;
00410
00411 return string_compare (a->path, b->path);
00412 }
00413
00414 void plugin_registry_prune (void)
00415 {
00416 g_list_foreach (plugin_list, (GFunc) plugin_prune, NULL);
00417 plugin_list = g_list_sort (plugin_list, (GCompareFunc) plugin_compare);
00418 registry_locked = TRUE;
00419 }
00420
00421 static gint plugin_lookup_cb (PluginHandle * plugin, const gchar * path)
00422 {
00423 return strcmp (plugin->path, path);
00424 }
00425
00426 PluginHandle * plugin_lookup (const gchar * path)
00427 {
00428 GList * node = g_list_find_custom (plugin_list, path, (GCompareFunc)
00429 plugin_lookup_cb);
00430 return node ? node->data : NULL;
00431 }
00432
00433 void plugin_register (const gchar * path)
00434 {
00435 PluginHandle * plugin = plugin_lookup (path);
00436 if (! plugin)
00437 {
00438 AUDDBG ("New plugin: %s\n", path);
00439 plugin_load (path);
00440 return;
00441 }
00442
00443 gint timestamp = file_get_mtime (path);
00444 g_return_if_fail (timestamp >= 0);
00445
00446 AUDDBG ("Register plugin: %s\n", path);
00447 plugin->confirmed = TRUE;
00448
00449 if (plugin->timestamp == timestamp)
00450 return;
00451
00452 AUDDBG ("Rescan plugin: %s\n", path);
00453 plugin->timestamp = timestamp;
00454 plugin_load (path);
00455 }
00456
00457 void plugin_register_loaded (const gchar * path, Plugin * header)
00458 {
00459 AUDDBG ("Loaded plugin: %s\n", path);
00460 PluginHandle * plugin = plugin_lookup (path);
00461 gboolean new = FALSE;
00462
00463 if (plugin)
00464 {
00465 g_return_if_fail (plugin->type == header->type);
00466
00467 plugin->loaded = TRUE;
00468 plugin->header = header;
00469
00470 if (registry_locked)
00471 return;
00472 }
00473 else
00474 {
00475 g_return_if_fail (! registry_locked);
00476
00477 gint timestamp = file_get_mtime (path);
00478 g_return_if_fail (timestamp >= 0);
00479
00480 plugin = plugin_new (g_strdup (path), TRUE, TRUE, timestamp,
00481 header->type, header);
00482 new = TRUE;
00483 }
00484
00485 g_free (plugin->name);
00486 plugin->name = g_strdup (header->name);
00487 plugin->has_about = PLUGIN_HAS_FUNC (header, about);
00488 plugin->has_configure = PLUGIN_HAS_FUNC (header, configure) ||
00489 PLUGIN_HAS_FUNC (header, settings);
00490
00491 if (header->type == PLUGIN_TYPE_TRANSPORT)
00492 {
00493 TransportPlugin * tp = (TransportPlugin *) header;
00494 for (gint i = 0; tp->schemes[i]; i ++)
00495 plugin->u.t.schemes = g_list_prepend (plugin->u.t.schemes, g_strdup
00496 (tp->schemes[i]));
00497 }
00498 else if (header->type == PLUGIN_TYPE_PLAYLIST)
00499 {
00500 PlaylistPlugin * pp = (PlaylistPlugin *) header;
00501 for (gint i = 0; pp->extensions[i]; i ++)
00502 plugin->u.p.exts = g_list_prepend (plugin->u.p.exts, g_strdup
00503 (pp->extensions[i]));
00504 }
00505 else if (header->type == PLUGIN_TYPE_INPUT)
00506 {
00507 InputPlugin * ip = (InputPlugin *) header;
00508 plugin->priority = ip->priority;
00509
00510 for (gint key = 0; key < INPUT_KEYS; key ++)
00511 {
00512 g_list_foreach (plugin->u.i.keys[key], (GFunc) g_free, NULL);
00513 g_list_free (plugin->u.i.keys[key]);
00514 plugin->u.i.keys[key] = NULL;
00515 }
00516
00517 if (PLUGIN_HAS_FUNC (ip, extensions))
00518 {
00519 for (gint i = 0; ip->extensions[i]; i ++)
00520 plugin->u.i.keys[INPUT_KEY_EXTENSION] = g_list_prepend
00521 (plugin->u.i.keys[INPUT_KEY_EXTENSION], g_strdup
00522 (ip->extensions[i]));
00523 }
00524
00525 if (PLUGIN_HAS_FUNC (ip, mimes))
00526 {
00527 for (gint i = 0; ip->mimes[i]; i ++)
00528 plugin->u.i.keys[INPUT_KEY_MIME] = g_list_prepend
00529 (plugin->u.i.keys[INPUT_KEY_MIME], g_strdup (ip->mimes[i]));
00530 }
00531
00532 if (PLUGIN_HAS_FUNC (ip, schemes))
00533 {
00534 for (gint i = 0; ip->schemes[i]; i ++)
00535 plugin->u.i.keys[INPUT_KEY_SCHEME] = g_list_prepend
00536 (plugin->u.i.keys[INPUT_KEY_SCHEME], g_strdup (ip->schemes[i]));
00537 }
00538
00539 plugin->u.i.has_images = PLUGIN_HAS_FUNC (ip, get_song_image);
00540 plugin->u.i.has_subtunes = ip->have_subtune;
00541 plugin->u.i.can_write_tuple = PLUGIN_HAS_FUNC (ip, update_song_tuple);
00542 plugin->u.i.has_infowin = PLUGIN_HAS_FUNC (ip, file_info_box);
00543 }
00544 else if (header->type == PLUGIN_TYPE_OUTPUT)
00545 {
00546 OutputPlugin * op = (OutputPlugin *) header;
00547 plugin->priority = 10 - op->probe_priority;
00548 }
00549 else if (header->type == PLUGIN_TYPE_EFFECT)
00550 {
00551 EffectPlugin * ep = (EffectPlugin *) header;
00552 plugin->priority = ep->order;
00553 }
00554 else if (header->type == PLUGIN_TYPE_GENERAL)
00555 {
00556 GeneralPlugin * gp = (GeneralPlugin *) header;
00557 if (new)
00558 plugin->enabled = gp->enabled_by_default;
00559 }
00560 }
00561
00562 gint plugin_get_type (PluginHandle * plugin)
00563 {
00564 return plugin->type;
00565 }
00566
00567 const gchar * plugin_get_filename (PluginHandle * plugin)
00568 {
00569 return plugin->path;
00570 }
00571
00572 const void * plugin_get_header (PluginHandle * plugin)
00573 {
00574 g_static_mutex_lock (& mutex);
00575
00576 if (! plugin->loaded)
00577 {
00578 plugin_load (plugin->path);
00579 plugin->loaded = TRUE;
00580 }
00581
00582 g_static_mutex_unlock (& mutex);
00583 return plugin->header;
00584 }
00585
00586 static gint plugin_by_header_cb (PluginHandle * plugin, const void * header)
00587 {
00588 return (plugin->header == header) ? 0 : -1;
00589 }
00590
00591 PluginHandle * plugin_by_header (const void * header)
00592 {
00593 GList * node = g_list_find_custom (plugin_list, header, (GCompareFunc)
00594 plugin_by_header_cb);
00595 return node ? node->data : NULL;
00596 }
00597
00598 void plugin_for_each (gint type, PluginForEachFunc func, void * data)
00599 {
00600 for (GList * node = plugin_list; node; node = node->next)
00601 {
00602 if (((PluginHandle *) node->data)->type != type)
00603 continue;
00604 if (! func (node->data, data))
00605 break;
00606 }
00607 }
00608
00609 const gchar * plugin_get_name (PluginHandle * plugin)
00610 {
00611 return plugin->name;
00612 }
00613
00614 gboolean plugin_has_about (PluginHandle * plugin)
00615 {
00616 return plugin->has_about;
00617 }
00618
00619 gboolean plugin_has_configure (PluginHandle * plugin)
00620 {
00621 return plugin->has_configure;
00622 }
00623
00624 gboolean plugin_get_enabled (PluginHandle * plugin)
00625 {
00626 return plugin->enabled;
00627 }
00628
00629 static void plugin_call_watches (PluginHandle * plugin)
00630 {
00631 for (GList * node = plugin->watches; node; )
00632 {
00633 GList * next = node->next;
00634 PluginWatch * watch = node->data;
00635
00636 if (! watch->func (plugin, watch->data))
00637 {
00638 g_free (watch);
00639 plugin->watches = g_list_delete_link (plugin->watches, node);
00640 }
00641
00642 node = next;
00643 }
00644 }
00645
00646 void plugin_set_enabled (PluginHandle * plugin, gboolean enabled)
00647 {
00648 plugin->enabled = enabled;
00649 plugin_call_watches (plugin);
00650 }
00651
00652 typedef struct {
00653 PluginForEachFunc func;
00654 void * data;
00655 } PluginForEnabledState;
00656
00657 static gboolean plugin_for_enabled_cb (PluginHandle * plugin,
00658 PluginForEnabledState * state)
00659 {
00660 if (! plugin->enabled)
00661 return TRUE;
00662 return state->func (plugin, state->data);
00663 }
00664
00665 void plugin_for_enabled (gint type, PluginForEachFunc func, void * data)
00666 {
00667 PluginForEnabledState state = {func, data};
00668 plugin_for_each (type, (PluginForEachFunc) plugin_for_enabled_cb, & state);
00669 }
00670
00671 void plugin_add_watch (PluginHandle * plugin, PluginForEachFunc func, void *
00672 data)
00673 {
00674 PluginWatch * watch = g_malloc (sizeof (PluginWatch));
00675 watch->func = func;
00676 watch->data = data;
00677 plugin->watches = g_list_prepend (plugin->watches, watch);
00678 }
00679
00680 void plugin_remove_watch (PluginHandle * plugin, PluginForEachFunc func, void *
00681 data)
00682 {
00683 for (GList * node = plugin->watches; node; )
00684 {
00685 GList * next = node->next;
00686 PluginWatch * watch = node->data;
00687
00688 if (watch->func == func && watch->data == data)
00689 {
00690 g_free (watch);
00691 plugin->watches = g_list_delete_link (plugin->watches, node);
00692 }
00693
00694 node = next;
00695 }
00696 }
00697
00698 typedef struct {
00699 const gchar * scheme;
00700 PluginHandle * plugin;
00701 } TransportPluginForSchemeState;
00702
00703 static gboolean transport_plugin_for_scheme_cb (PluginHandle * plugin,
00704 TransportPluginForSchemeState * state)
00705 {
00706 if (! g_list_find_custom (plugin->u.t.schemes, state->scheme, (GCompareFunc)
00707 strcasecmp))
00708 return TRUE;
00709
00710 state->plugin = plugin;
00711 return FALSE;
00712 }
00713
00714 PluginHandle * transport_plugin_for_scheme (const gchar * scheme)
00715 {
00716 TransportPluginForSchemeState state = {scheme, NULL};
00717 plugin_for_enabled (PLUGIN_TYPE_TRANSPORT, (PluginForEachFunc)
00718 transport_plugin_for_scheme_cb, & state);
00719 return state.plugin;
00720 }
00721
00722 typedef struct {
00723 const gchar * ext;
00724 PluginHandle * plugin;
00725 } PlaylistPluginForExtState;
00726
00727 static gboolean playlist_plugin_for_ext_cb (PluginHandle * plugin,
00728 PlaylistPluginForExtState * state)
00729 {
00730 if (! g_list_find_custom (plugin->u.p.exts, state->ext, (GCompareFunc)
00731 strcasecmp))
00732 return TRUE;
00733
00734 state->plugin = plugin;
00735 return FALSE;
00736 }
00737
00738 PluginHandle * playlist_plugin_for_extension (const gchar * extension)
00739 {
00740 PlaylistPluginForExtState state = {extension, NULL};
00741 plugin_for_enabled (PLUGIN_TYPE_PLAYLIST, (PluginForEachFunc)
00742 playlist_plugin_for_ext_cb, & state);
00743 return state.plugin;
00744 }
00745
00746 typedef struct {
00747 gint key;
00748 const gchar * value;
00749 PluginForEachFunc func;
00750 void * data;
00751 } InputPluginForKeyState;
00752
00753 static gboolean input_plugin_for_key_cb (PluginHandle * plugin,
00754 InputPluginForKeyState * state)
00755 {
00756 if (! g_list_find_custom (plugin->u.i.keys[state->key], state->value,
00757 (GCompareFunc) strcasecmp))
00758 return TRUE;
00759
00760 return state->func (plugin, state->data);
00761 }
00762
00763 void input_plugin_for_key (gint key, const gchar * value, PluginForEachFunc
00764 func, void * data)
00765 {
00766 InputPluginForKeyState state = {key, value, func, data};
00767 plugin_for_enabled (PLUGIN_TYPE_INPUT, (PluginForEachFunc)
00768 input_plugin_for_key_cb, & state);
00769 }
00770
00771 gboolean input_plugin_has_images (PluginHandle * plugin)
00772 {
00773 g_return_val_if_fail (plugin->type == PLUGIN_TYPE_INPUT, FALSE);
00774 return plugin->u.i.has_images;
00775 }
00776
00777 gboolean input_plugin_has_subtunes (PluginHandle * plugin)
00778 {
00779 g_return_val_if_fail (plugin->type == PLUGIN_TYPE_INPUT, FALSE);
00780 return plugin->u.i.has_subtunes;
00781 }
00782
00783 gboolean input_plugin_can_write_tuple (PluginHandle * plugin)
00784 {
00785 g_return_val_if_fail (plugin->type == PLUGIN_TYPE_INPUT, FALSE);
00786 return plugin->u.i.can_write_tuple;
00787 }
00788
00789 gboolean input_plugin_has_infowin (PluginHandle * plugin)
00790 {
00791 g_return_val_if_fail (plugin->type == PLUGIN_TYPE_INPUT, FALSE);
00792 return plugin->u.i.has_infowin;
00793 }