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