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