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 "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 }