00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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
00189
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 }