Audacious
$Id:Doxyfile42802007-03-2104:39:00Znenolod$
|
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 }