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 "main.h" 00032 #include "misc.h" 00033 #include "plugin.h" 00034 #include "pluginenum.h" 00035 #include "plugins.h" 00036 #include "util.h" 00037 00038 #define FILENAME "plugin-registry" 00039 #define FORMAT 2 00040 00041 typedef struct { 00042 gchar * path; 00043 gboolean confirmed; 00044 gint timestamp; 00045 gboolean loaded; 00046 GList * plugin_list; 00047 } ModuleData; 00048 00049 typedef struct { 00050 GList * keys[INPUT_KEYS]; 00051 } InputPluginData; 00052 00053 struct PluginHandle { 00054 ModuleData * module; 00055 gint type, number; 00056 gboolean confirmed; 00057 void * header; 00058 gchar * name; 00059 gint priority; 00060 gboolean has_about, has_configure, enabled; 00061 00062 union { 00063 InputPluginData i; 00064 } u; 00065 }; 00066 00067 static const gchar * plugin_type_names[] = { 00068 [PLUGIN_TYPE_BASIC] = NULL, 00069 [PLUGIN_TYPE_INPUT] = "input", 00070 [PLUGIN_TYPE_OUTPUT] = "output", 00071 [PLUGIN_TYPE_EFFECT] = "effect", 00072 [PLUGIN_TYPE_VIS] = "vis", 00073 [PLUGIN_TYPE_IFACE] = "iface", 00074 [PLUGIN_TYPE_GENERAL] = "general"}; 00075 static const gchar * input_key_names[] = { 00076 [INPUT_KEY_SCHEME] = "scheme", 00077 [INPUT_KEY_EXTENSION] = "ext", 00078 [INPUT_KEY_MIME] = "mime"}; 00079 static GList * module_list = NULL; 00080 static GList * plugin_list = NULL; 00081 static gboolean registry_locked = TRUE; 00082 00083 static ModuleData * module_new (gchar * path, gboolean confirmed, gint 00084 timestamp, gboolean loaded) 00085 { 00086 ModuleData * module = g_malloc (sizeof (ModuleData)); 00087 00088 module->path = path; 00089 module->confirmed = confirmed; 00090 module->timestamp = timestamp; 00091 module->loaded = loaded; 00092 module->plugin_list = NULL; 00093 00094 module_list = g_list_prepend (module_list, module); 00095 00096 return module; 00097 } 00098 00099 static PluginHandle * plugin_new (ModuleData * module, gint type, gint number, 00100 gboolean confirmed, void * header) 00101 { 00102 PluginHandle * plugin = g_malloc (sizeof (PluginHandle)); 00103 00104 plugin->module = module; 00105 plugin->type = type; 00106 plugin->number = number; 00107 plugin->confirmed = confirmed; 00108 plugin->header = header; 00109 plugin->name = NULL; 00110 plugin->priority = 0; 00111 plugin->has_about = FALSE; 00112 plugin->has_configure = FALSE; 00113 plugin->enabled = FALSE; 00114 00115 if (type == PLUGIN_TYPE_INPUT) 00116 { 00117 plugin->enabled = TRUE; 00118 memset (plugin->u.i.keys, 0, sizeof plugin->u.i.keys); 00119 } 00120 else if (type == PLUGIN_TYPE_IFACE) 00121 plugin->enabled = TRUE; 00122 00123 plugin_list = g_list_prepend (plugin_list, plugin); 00124 module->plugin_list = g_list_prepend (module->plugin_list, plugin); 00125 00126 return plugin; 00127 } 00128 00129 static void plugin_free (PluginHandle * plugin, ModuleData * module) 00130 { 00131 plugin_list = g_list_remove (plugin_list, plugin); 00132 module->plugin_list = g_list_remove (module->plugin_list, plugin); 00133 00134 if (plugin->type == PLUGIN_TYPE_INPUT) 00135 { 00136 for (gint key = 0; key < INPUT_KEYS; key ++) 00137 { 00138 g_list_foreach (plugin->u.i.keys[key], (GFunc) g_free, NULL); 00139 g_list_free (plugin->u.i.keys[key]); 00140 } 00141 } 00142 00143 g_free (plugin->name); 00144 g_free (plugin); 00145 } 00146 00147 static void module_free (ModuleData * module) 00148 { 00149 module_list = g_list_remove (module_list, module); 00150 00151 g_list_foreach (module->plugin_list, (GFunc) plugin_free, module); 00152 00153 g_free (module->path); 00154 g_free (module); 00155 } 00156 00157 static FILE * open_registry_file (const gchar * mode) 00158 { 00159 gchar path[PATH_MAX]; 00160 snprintf (path, sizeof path, "%s/" FILENAME, aud_paths[BMP_PATH_USER_DIR]); 00161 return fopen (path, mode); 00162 } 00163 00164 static void input_plugin_save (PluginHandle * plugin, FILE * handle) 00165 { 00166 for (gint key = 0; key < INPUT_KEYS; key ++) 00167 { 00168 for (GList * node = plugin->u.i.keys[key]; node != NULL; node = 00169 node->next) 00170 fprintf (handle, "%s %s\n", input_key_names[key], (const gchar *) 00171 node->data); 00172 } 00173 } 00174 00175 static void plugin_save (PluginHandle * plugin, FILE * handle) 00176 { 00177 fprintf (handle, "%s %d\n", plugin_type_names[plugin->type], plugin->number); 00178 fprintf (handle, "name %s\n", plugin->name); 00179 fprintf (handle, "priority %d\n", plugin->priority); 00180 fprintf (handle, "about %d\n", plugin->has_about); 00181 fprintf (handle, "config %d\n", plugin->has_configure); 00182 fprintf (handle, "enabled %d\n", plugin->enabled); 00183 00184 if (plugin->type == PLUGIN_TYPE_INPUT) 00185 input_plugin_save (plugin, handle); 00186 } 00187 00188 /* If the module contains any plugins that we do not handle, we do not save it, 00189 * thereby forcing it to be loaded on next startup. */ 00190 static gint plugin_not_handled_cb (PluginHandle * plugin) 00191 { 00192 return (plugin_type_names[plugin->type] == NULL) ? 0 : -1; 00193 } 00194 00195 static void module_save (ModuleData * module, FILE * handle) 00196 { 00197 if (g_list_find_custom (module->plugin_list, NULL, (GCompareFunc) 00198 plugin_not_handled_cb) != NULL) 00199 return; 00200 00201 fprintf (handle, "module %s\n", module->path); 00202 fprintf (handle, "stamp %d\n", module->timestamp); 00203 00204 g_list_foreach (module->plugin_list, (GFunc) plugin_save, handle); 00205 } 00206 00207 void plugin_registry_save (void) 00208 { 00209 FILE * handle = open_registry_file ("w"); 00210 g_return_if_fail (handle != NULL); 00211 00212 fprintf (handle, "format %d\n", FORMAT); 00213 00214 g_list_foreach (module_list, (GFunc) module_save, handle); 00215 fclose (handle); 00216 00217 g_list_foreach (module_list, (GFunc) module_free, NULL); 00218 registry_locked = TRUE; 00219 } 00220 00221 static gchar parse_key[512]; 00222 static gchar * parse_value; 00223 00224 static void parse_next (FILE * handle) 00225 { 00226 parse_value = NULL; 00227 00228 if (fgets (parse_key, sizeof parse_key, handle) == NULL) 00229 return; 00230 00231 gchar * space = strchr (parse_key, ' '); 00232 if (space == NULL) 00233 return; 00234 00235 * space = 0; 00236 parse_value = space + 1; 00237 00238 gchar * newline = strchr (parse_value, '\n'); 00239 if (newline != NULL) 00240 * newline = 0; 00241 } 00242 00243 static gboolean parse_integer (const gchar * key, gint * value) 00244 { 00245 return (parse_value != NULL && ! strcmp (parse_key, key) && sscanf 00246 (parse_value, "%d", value) == 1); 00247 } 00248 00249 static gchar * parse_string (const gchar * key) 00250 { 00251 return (parse_value != NULL && ! strcmp (parse_key, key)) ? g_strdup 00252 (parse_value) : NULL; 00253 } 00254 00255 static void input_plugin_parse (PluginHandle * plugin, FILE * handle) 00256 { 00257 for (gint key = 0; key < INPUT_KEYS; key ++) 00258 { 00259 gchar * value; 00260 while ((value = parse_string (input_key_names[key])) != NULL) 00261 { 00262 plugin->u.i.keys[key] = g_list_prepend (plugin->u.i.keys[key], 00263 value); 00264 parse_next (handle); 00265 } 00266 } 00267 } 00268 00269 static gboolean plugin_parse (ModuleData * module, FILE * handle) 00270 { 00271 gint type, number; 00272 for (type = 0; type < PLUGIN_TYPES; type ++) 00273 { 00274 if (plugin_type_names[type] != NULL && parse_integer 00275 (plugin_type_names[type], & number)) 00276 goto FOUND; 00277 } 00278 00279 return FALSE; 00280 00281 FOUND:; 00282 PluginHandle * plugin = plugin_new (module, type, number, FALSE, NULL); 00283 parse_next (handle); 00284 00285 if ((plugin->name = parse_string ("name")) != NULL) 00286 parse_next (handle); 00287 if (parse_integer ("priority", & plugin->priority)) 00288 parse_next (handle); 00289 if (parse_integer ("about", & plugin->has_about)) 00290 parse_next (handle); 00291 if (parse_integer ("config", & plugin->has_configure)) 00292 parse_next (handle); 00293 if (parse_integer ("enabled", & plugin->enabled)) 00294 parse_next (handle); 00295 00296 if (type == PLUGIN_TYPE_INPUT) 00297 input_plugin_parse (plugin, handle); 00298 00299 return TRUE; 00300 } 00301 00302 static gboolean module_parse (FILE * handle) 00303 { 00304 gchar * path = parse_string ("module"); 00305 if (path == NULL) 00306 return FALSE; 00307 00308 parse_next (handle); 00309 00310 gint timestamp; 00311 if (! parse_integer ("stamp", & timestamp)) 00312 { 00313 g_free (path); 00314 return FALSE; 00315 } 00316 00317 ModuleData * module = module_new (path, FALSE, timestamp, FALSE); 00318 parse_next (handle); 00319 00320 while (plugin_parse (module, handle)) 00321 ; 00322 00323 return TRUE; 00324 } 00325 00326 void plugin_registry_load (void) 00327 { 00328 FILE * handle = open_registry_file ("r"); 00329 if (handle == NULL) 00330 goto UNLOCK; 00331 00332 parse_next (handle); 00333 00334 gint format; 00335 if (! parse_integer ("format", & format) || format != FORMAT) 00336 goto ERROR; 00337 00338 parse_next (handle); 00339 00340 while (module_parse (handle)) 00341 ; 00342 00343 ERROR: 00344 fclose (handle); 00345 UNLOCK: 00346 registry_locked = FALSE; 00347 } 00348 00349 static void plugin_prune (PluginHandle * plugin, ModuleData * module) 00350 { 00351 if (plugin->confirmed) 00352 return; 00353 00354 AUDDBG ("Plugin not found: %s %d:%d\n", plugin->module->path, plugin->type, 00355 plugin->number); 00356 plugin_free (plugin, module); 00357 } 00358 00359 static void module_prune (ModuleData * module) 00360 { 00361 if (! module->confirmed) 00362 { 00363 AUDDBG ("Module not found: %s\n", module->path); 00364 module_free (module); 00365 return; 00366 } 00367 00368 if (module->loaded) 00369 g_list_foreach (module->plugin_list, (GFunc) plugin_prune, module); 00370 } 00371 00372 gint plugin_compare (PluginHandle * a, PluginHandle * b) 00373 { 00374 if (a->type < b->type) 00375 return -1; 00376 if (a->type > b->type) 00377 return 1; 00378 if (a->priority < b->priority) 00379 return -1; 00380 if (a->priority > b->priority) 00381 return 1; 00382 00383 gint diff; 00384 if ((diff = string_compare (a->name, b->name))) 00385 return diff; 00386 if ((diff = string_compare (a->module->path, b->module->path))) 00387 return diff; 00388 00389 if (a->number < b->number) 00390 return -1; 00391 if (a->number > b->number) 00392 return 1; 00393 00394 return 0; 00395 } 00396 00397 void plugin_registry_prune (void) 00398 { 00399 g_list_foreach (module_list, (GFunc) module_prune, NULL); 00400 plugin_list = g_list_sort (plugin_list, (GCompareFunc) plugin_compare); 00401 registry_locked = TRUE; 00402 } 00403 00404 static gint module_lookup_cb (ModuleData * module, const gchar * path) 00405 { 00406 return strcmp (module->path, path); 00407 } 00408 00409 static ModuleData * module_lookup (const gchar * path) 00410 { 00411 GList * node = g_list_find_custom (module_list, path, (GCompareFunc) 00412 module_lookup_cb); 00413 return (node != NULL) ? node->data : NULL; 00414 } 00415 00416 void module_register (const gchar * path) 00417 { 00418 gint timestamp = file_get_mtime (path); 00419 g_return_if_fail (timestamp >= 0); 00420 00421 ModuleData * module = module_lookup (path); 00422 if (module == NULL) 00423 { 00424 AUDDBG ("New module: %s\n", path); 00425 g_return_if_fail (! registry_locked); 00426 module = module_new (g_strdup (path), TRUE, timestamp, TRUE); 00427 module_load (path); 00428 module->loaded = TRUE; 00429 return; 00430 } 00431 00432 AUDDBG ("Register module: %s\n", path); 00433 module->confirmed = TRUE; 00434 if (module->timestamp == timestamp) 00435 return; 00436 00437 AUDDBG ("Rescan module: %s\n", path); 00438 module->timestamp = timestamp; 00439 module_load (path); 00440 module->loaded = TRUE; 00441 } 00442 00443 typedef struct { 00444 gint type, number; 00445 } PluginLookupState; 00446 00447 static gint plugin_lookup_cb (PluginHandle * plugin, PluginLookupState * state) 00448 { 00449 return (plugin->type == state->type && plugin->number == state->number) ? 0 00450 : -1; 00451 } 00452 00453 static PluginHandle * plugin_lookup (ModuleData * module, gint type, gint number) 00454 { 00455 PluginLookupState state = {type, number}; 00456 GList * node = g_list_find_custom (module->plugin_list, & state, 00457 (GCompareFunc) plugin_lookup_cb); 00458 return (node != NULL) ? node->data : NULL; 00459 } 00460 00461 void plugin_register (const gchar * path, gint type, gint number, void * header) 00462 { 00463 ModuleData * module = module_lookup (path); 00464 g_return_if_fail (module != NULL); 00465 00466 PluginHandle * plugin = plugin_lookup (module, type, number); 00467 if (plugin == NULL) 00468 { 00469 AUDDBG ("New plugin: %s %d:%d\n", path, type, number); 00470 g_return_if_fail (! registry_locked); 00471 plugin = plugin_new (module, type, number, TRUE, header); 00472 if (type == PLUGIN_TYPE_GENERAL) { 00473 if (path && g_strrstr(path,"gnomeshortcuts.so")!=NULL) { 00474 plugin->enabled = TRUE; 00475 } 00476 } 00477 } 00478 00479 AUDDBG ("Register plugin: %s %d:%d\n", path, type, number); 00480 plugin->confirmed = TRUE; 00481 plugin->header = header; 00482 00483 if (type == PLUGIN_TYPE_INPUT) 00484 { 00485 InputPlugin * ip = header; 00486 g_free (plugin->name); 00487 plugin->name = g_strdup (ip->description); 00488 plugin->priority = ip->priority; 00489 plugin->has_about = (ip->about != NULL); 00490 plugin->has_configure = (ip->configure != NULL); 00491 00492 for (gint key = 0; key < INPUT_KEYS; key ++) 00493 { 00494 g_list_foreach (plugin->u.i.keys[key], (GFunc) g_free, NULL); 00495 g_list_free (plugin->u.i.keys[key]); 00496 plugin->u.i.keys[key] = NULL; 00497 } 00498 00499 if (ip->vfs_extensions != NULL) 00500 { 00501 for (gint i = 0; ip->vfs_extensions[i] != NULL; i ++) 00502 plugin->u.i.keys[INPUT_KEY_EXTENSION] = g_list_prepend 00503 (plugin->u.i.keys[INPUT_KEY_EXTENSION], g_strdup 00504 (ip->vfs_extensions[i])); 00505 } 00506 } 00507 else if (type == PLUGIN_TYPE_OUTPUT) 00508 { 00509 OutputPlugin * op = header; 00510 g_free (plugin->name); 00511 plugin->name = g_strdup (op->description); 00512 plugin->priority = 10 - op->probe_priority; 00513 plugin->has_about = (op->about != NULL); 00514 plugin->has_configure = (op->configure != NULL); 00515 } 00516 else if (type == PLUGIN_TYPE_EFFECT) 00517 { 00518 EffectPlugin * ep = header; 00519 g_free (plugin->name); 00520 plugin->name = g_strdup (ep->description); 00521 plugin->priority = ep->order; 00522 plugin->has_about = (ep->about != NULL); 00523 plugin->has_configure = (ep->configure != NULL); 00524 } 00525 else if (type == PLUGIN_TYPE_VIS) 00526 { 00527 VisPlugin * vp = header; 00528 g_free (plugin->name); 00529 plugin->name = g_strdup (vp->description); 00530 plugin->has_about = (vp->about != NULL); 00531 plugin->has_configure = (vp->configure != NULL); 00532 } 00533 else if (type == PLUGIN_TYPE_IFACE) 00534 { 00535 Interface * i = header; 00536 g_free (plugin->name); 00537 plugin->name = g_strdup (i->desc); 00538 } 00539 else if (type == PLUGIN_TYPE_GENERAL) 00540 { 00541 GeneralPlugin * gp = header; 00542 g_free (plugin->name); 00543 plugin->name = g_strdup (gp->description); 00544 plugin->has_about = (gp->about != NULL); 00545 plugin->has_configure = (gp->configure != NULL); 00546 } 00547 } 00548 00549 void plugin_get_path (PluginHandle * plugin, const gchar * * path, gint * type, 00550 gint * number) 00551 { 00552 * path = plugin->module->path; 00553 * type = plugin->type; 00554 * number = plugin->number; 00555 } 00556 00557 PluginHandle * plugin_by_path (const gchar * path, gint type, gint number) 00558 { 00559 ModuleData * module = module_lookup (path); 00560 if (module == NULL) 00561 return NULL; 00562 00563 return plugin_lookup (module, type, number); 00564 } 00565 00566 void * plugin_get_header (PluginHandle * plugin) 00567 { 00568 if (! plugin->module->loaded) 00569 { 00570 module_load (plugin->module->path); 00571 plugin->module->loaded = TRUE; 00572 } 00573 00574 return plugin->header; 00575 } 00576 00577 static gint plugin_by_header_cb (PluginHandle * plugin, void * header) 00578 { 00579 return (plugin->header == header) ? 0 : -1; 00580 } 00581 00582 PluginHandle * plugin_by_header (void * header) 00583 { 00584 GList * node = g_list_find_custom (plugin_list, header, (GCompareFunc) 00585 plugin_by_header_cb); 00586 return (node != NULL) ? node->data : NULL; 00587 } 00588 00589 void plugin_for_each (gint type, PluginForEachFunc func, void * data) 00590 { 00591 for (GList * node = plugin_list; node != NULL; node = node->next) 00592 { 00593 if (((PluginHandle *) node->data)->type != type) 00594 continue; 00595 if (! func (node->data, data)) 00596 break; 00597 } 00598 } 00599 00600 const gchar * plugin_get_name (PluginHandle * plugin) 00601 { 00602 return plugin->name; 00603 } 00604 00605 gboolean plugin_has_about (PluginHandle * plugin) 00606 { 00607 return plugin->has_about; 00608 } 00609 00610 gboolean plugin_has_configure (PluginHandle * plugin) 00611 { 00612 return plugin->has_configure; 00613 } 00614 00615 gboolean plugin_get_enabled (PluginHandle * plugin) 00616 { 00617 return plugin->enabled; 00618 } 00619 00620 void plugin_set_enabled (PluginHandle * plugin, gboolean enabled) 00621 { 00622 plugin->enabled = enabled; 00623 } 00624 00625 typedef struct { 00626 PluginForEachFunc func; 00627 void * data; 00628 } PluginForEnabledState; 00629 00630 static gboolean plugin_for_enabled_cb (PluginHandle * plugin, 00631 PluginForEnabledState * state) 00632 { 00633 if (! plugin->enabled) 00634 return TRUE; 00635 return state->func (plugin, state->data); 00636 } 00637 00638 void plugin_for_enabled (gint type, PluginForEachFunc func, void * data) 00639 { 00640 PluginForEnabledState state = {func, data}; 00641 plugin_for_each (type, (PluginForEachFunc) plugin_for_enabled_cb, & state); 00642 } 00643 00644 typedef struct { 00645 gint key; 00646 const gchar * value; 00647 PluginForEachFunc func; 00648 void * data; 00649 } InputPluginForKeyState; 00650 00651 static gboolean input_plugin_for_key_cb (PluginHandle * plugin, 00652 InputPluginForKeyState * state) 00653 { 00654 if (g_list_find_custom (plugin->u.i.keys[state->key], state->value, 00655 (GCompareFunc) strcasecmp) == NULL) 00656 return TRUE; 00657 00658 return state->func (plugin, state->data); 00659 } 00660 00661 void input_plugin_for_key (gint key, const gchar * value, PluginForEachFunc 00662 func, void * data) 00663 { 00664 InputPluginForKeyState state = {key, value, func, data}; 00665 plugin_for_enabled (PLUGIN_TYPE_INPUT, (PluginForEachFunc) 00666 input_plugin_for_key_cb, & state); 00667 } 00668 00669 static void input_plugin_add_key (InputPlugin * header, gint key, const gchar * 00670 value) 00671 { 00672 PluginHandle * plugin = plugin_by_header (header); 00673 g_return_if_fail (plugin != NULL); 00674 plugin->u.i.keys[key] = g_list_prepend (plugin->u.i.keys[key], g_strdup 00675 (value)); 00676 } 00677 00678 void uri_set_plugin (const gchar * scheme, InputPlugin * header) 00679 { 00680 input_plugin_add_key (header, INPUT_KEY_SCHEME, scheme); 00681 } 00682 00683 void mime_set_plugin (const gchar * mime, InputPlugin * header) 00684 { 00685 input_plugin_add_key (header, INPUT_KEY_MIME, mime); 00686 }