Audacious  $Id:Doxyfile42802007-03-2104:39:00Znenolod$
plugin-registry.c
Go to the documentation of this file.
00001 /*
00002  * plugin-registry.c
00003  * Copyright 2009-2011 John Lindgren
00004  *
00005  * This file is part of Audacious.
00006  *
00007  * Audacious is free software: you can redistribute it and/or modify it under
00008  * the terms of the GNU General Public License as published by the Free Software
00009  * Foundation, version 2 or version 3 of the License.
00010  *
00011  * Audacious is distributed in the hope that it will be useful, but WITHOUT ANY
00012  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
00013  * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License along with
00016  * Audacious. If not, see <http://www.gnu.org/licenses/>.
00017  *
00018  * The Audacious team does not consider modular code linking to Audacious or
00019  * using our public API to be a derived work.
00020  */
00021 
00022 /* While the registry is being built (during early startup) or destroyed (during
00023  * late shutdown), the registry_locked flag will be set.  Once this flag is
00024  * cleared, the registry will not be modified and can be read by concurrent
00025  * threads.  The one change that can happen during this time is that a plugin is
00026  * loaded; hence the mutex must be locked before checking that a plugin is
00027  * loaded and while loading it. */
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 /* Note: If there are multiple plugins with the same basename, this returns only
00449  * one of them. So give different plugins different basenames. --jlindgren */
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 }