Files
evolution/e-util/e-plugin.c
Matthew Barnes 381c91c0d2 Bug 675391 - Remove support for EVOLUTION_PLUGIN_PATH.
This was a hidden feature to load EPlugins from a custom location by
setting the EVOLUTION_PLUGIN_PATH environment variable.  It defaulted
to $HOME/.eplugins, which is in violation of the XDG Base Directory
Specification.

Since I've never ever heard of anyone using this and wasn't even aware
of it myself, rather than migrating the folder to a standard-compliant
location I'm just going to drop support for EVOLUTION_PLUGIN_PATH and
put my money on no one noticing.  The EPlugin framework is gradually
being decommissioned anyway in favor of the simpler and more flexible
EExtension framework in Evolution-Data-Server.
2012-05-03 16:09:48 -04:00

993 lines
22 KiB
C

/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with the program; if not, see <http://www.gnu.org/licenses/>
*
*
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <sys/types.h>
#include <string.h>
#include <glib/gi18n.h>
#include <libebackend/e-module.h>
#include <libedataserver/e-data-server-util.h>
#include <libedataserver/e-xml-utils.h>
#include "e-plugin.h"
#include "e-util-private.h"
/* plugin debug */
#define pd(x)
/* plugin hook debug */
#define phd(x)
/*
* <camel-plugin
* class="org.gnome.camel.plugin.provider:1.0"
* id="org.gnome.camel.provider.imap:1.0"
* type="shlib"
* location="/opt/gnome2/lib/camel/1.0/libcamelimap.so"
* factory="camel_imap_provider_new">
* <name>imap</name>
* <description>IMAP4 and IMAP4v1 mail store</description>
* <class-data class="org.gnome.camel.plugin.provider:1.0"
* protocol="imap"
* domain="mail"
* flags="remote,source,storage,ssl"/>
* </camel-plugin>
*
* <camel-plugin
* class="org.gnome.camel.plugin.sasl:1.0"
* id="org.gnome.camel.sasl.plain:1.0"
* type="shlib"
* location="/opt/gnome2/lib/camel/1.0/libcamelsasl.so"
* factory="camel_sasl_plain_new">
* <name>PLAIN</name>
* <description>SASL PLAIN authentication mechanism</description>
* </camel-plugin>
*/
/* EPlugin stuff */
/* global table of plugin types by pluginclass.type */
static GHashTable *ep_types;
/* global table of plugins by plugin.id */
static GHashTable *ep_plugins;
/* the list of disabled plugins from GSettings */
static GSList *ep_disabled;
/* All classes which implement EPluginHooks, by class.id */
static GHashTable *eph_types;
struct _plugin_doc {
struct _plugin_doc *next;
struct _plugin_doc *prev;
gchar *filename;
xmlDocPtr doc;
};
enum {
EP_PROP_0,
EP_PROP_ENABLED
};
G_DEFINE_TYPE (
EPlugin,
e_plugin,
G_TYPE_OBJECT)
static gboolean
ep_check_enabled (const gchar *id)
{
/* Return TRUE if 'id' is NOT in the disabled list. */
return !g_slist_find_custom (ep_disabled, id, (GCompareFunc) strcmp);
}
static void
ep_set_enabled (const gchar *id,
gint state)
{
GSettings *settings;
GSList *link;
GPtrArray *array;
/* Bail out if no change to state, when expressed as a boolean: */
if ((state == 0) == (ep_check_enabled (id) == 0))
return;
if (state) {
GSList *link;
link = g_slist_find_custom (
ep_disabled, id, (GCompareFunc) strcmp);
if (link != NULL) {
g_free (link->data);
ep_disabled = g_slist_remove_link (ep_disabled, link);
}
} else
ep_disabled = g_slist_prepend (ep_disabled, g_strdup (id));
settings = g_settings_new ("org.gnome.evolution");
array = g_ptr_array_new ();
for (link = ep_disabled; link != NULL; link = link->next)
g_ptr_array_add (array, link->data);
g_ptr_array_add (array, NULL);
g_settings_set_strv (
settings, "disabled-eplugins",
(const gchar * const *) array->pdata);
g_ptr_array_free (array, TRUE);
g_object_unref (settings);
}
static gint
ep_construct (EPlugin *ep,
xmlNodePtr root)
{
xmlNodePtr node;
gint res = -1;
gchar *localedir;
ep->domain = e_plugin_xml_prop(root, "domain");
if (ep->domain
&& (localedir = e_plugin_xml_prop(root, "localedir"))) {
#ifdef G_OS_WIN32
gchar *mapped_localedir =
e_util_replace_prefix (EVOLUTION_PREFIX,
e_util_get_prefix (),
localedir);
g_free (localedir);
localedir = mapped_localedir;
#endif
bindtextdomain (ep->domain, localedir);
g_free (localedir);
}
ep->name = e_plugin_xml_prop_domain(root, "name", ep->domain);
node = root->children;
while (node) {
if (strcmp((gchar *)node->name, "hook") == 0) {
EPluginHook *hook;
EPluginHookClass *type;
gchar *class = e_plugin_xml_prop(node, "class");
if (class == NULL) {
g_warning (
"Plugin '%s' load failed in '%s', "
"missing class property for hook",
ep->id, ep->path);
goto fail;
}
if (ep->enabled
&& eph_types != NULL
&& (type = g_hash_table_lookup (
eph_types, class)) != NULL) {
g_free (class);
hook = g_object_new (G_OBJECT_CLASS_TYPE (type), NULL);
res = type->construct (hook, ep, node);
if (res == -1) {
g_warning (
"Plugin '%s' failed to "
"load hook", ep->name);
g_object_unref (hook);
goto fail;
} else {
ep->hooks = g_slist_append (ep->hooks, hook);
}
} else {
g_free (class);
}
} else if (strcmp((gchar *)node->name, "description") == 0) {
ep->description =
e_plugin_xml_content_domain (node, ep->domain);
} else if (strcmp((gchar *)node->name, "author") == 0) {
gchar *name = e_plugin_xml_prop(node, "name");
gchar *email = e_plugin_xml_prop(node, "email");
if (name || email) {
EPluginAuthor *epa = g_malloc0 (sizeof (*epa));
epa->name = name;
epa->email = email;
ep->authors = g_slist_append (ep->authors, epa);
}
}
node = node->next;
}
res = 0;
fail:
return res;
}
static void
ep_enable (EPlugin *ep,
gint state)
{
GSList *iter;
ep->enabled = state;
for (iter = ep->hooks; iter != NULL; iter = iter->next) {
EPluginHook *hook = iter->data;
e_plugin_hook_enable (hook, state);
}
ep_set_enabled (ep->id, state);
}
static void
ep_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
switch (property_id) {
case EP_PROP_ENABLED:
e_plugin_enable (
E_PLUGIN (object),
g_value_get_boolean (value));
return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
ep_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
EPlugin *ep = E_PLUGIN (object);
switch (property_id) {
case EP_PROP_ENABLED:
g_value_set_boolean (value, ep->enabled);
return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
ep_finalize (GObject *object)
{
EPlugin *ep = E_PLUGIN (object);
g_free (ep->id);
g_free (ep->description);
g_free (ep->name);
g_free (ep->domain);
g_slist_foreach (ep->hooks, (GFunc) g_object_unref, NULL);
g_slist_free (ep->hooks);
/* Chain up to parent's finalize() method. */
G_OBJECT_CLASS (e_plugin_parent_class)->finalize (object);
}
static void
e_plugin_class_init (EPluginClass *class)
{
GObjectClass *object_class;
object_class = G_OBJECT_CLASS (class);
object_class->set_property = ep_set_property;
object_class->get_property = ep_get_property;
object_class->finalize = ep_finalize;
class->construct = ep_construct;
class->enable = ep_enable;
g_object_class_install_property (
object_class,
EP_PROP_ENABLED,
g_param_spec_boolean (
"enabled",
"Enabled",
"Whether the plugin is enabled",
TRUE,
G_PARAM_READWRITE));
}
static void
e_plugin_init (EPlugin *ep)
{
ep->enabled = TRUE;
}
static EPlugin *
ep_load_plugin (xmlNodePtr root,
struct _plugin_doc *pdoc)
{
gchar *prop, *id;
EPluginClass *class;
EPlugin *ep;
id = e_plugin_xml_prop(root, "id");
if (id == NULL) {
g_warning("Invalid e-plugin entry in '%s': no id", pdoc->filename);
return NULL;
}
if (g_hash_table_lookup (ep_plugins, id)) {
g_warning("Plugin '%s' already defined", id);
g_free (id);
return NULL;
}
prop = (gchar *)xmlGetProp(root, (const guchar *)"type");
if (prop == NULL) {
g_free (id);
g_warning("Invalid e-plugin entry in '%s': no type", pdoc->filename);
return NULL;
}
/* If we can't find a plugin, add it to a pending list
* which is checked when a new type is registered. */
class = g_hash_table_lookup (ep_types, prop);
if (class == NULL) {
g_free (id);
xmlFree (prop);
return NULL;
}
xmlFree (prop);
ep = g_object_new (G_TYPE_FROM_CLASS (class), NULL);
ep->id = id;
ep->path = g_strdup (pdoc->filename);
ep->enabled = ep_check_enabled (id);
if (e_plugin_construct (ep, root) == -1)
e_plugin_enable (ep, FALSE);
g_hash_table_insert (ep_plugins, ep->id, ep);
return ep;
}
static gint
ep_load (const gchar *filename,
gint load_level)
{
xmlDocPtr doc;
xmlNodePtr root;
EPlugin *ep = NULL;
struct _plugin_doc *pdoc;
doc = e_xml_parse_file (filename);
if (doc == NULL)
return -1;
root = xmlDocGetRootElement (doc);
if (strcmp((gchar *)root->name, "e-plugin-list") != 0) {
g_warning("No <e-plugin-list> root element: %s", filename);
xmlFreeDoc (doc);
return -1;
}
pdoc = g_malloc0 (sizeof (*pdoc));
pdoc->doc = doc;
pdoc->filename = g_strdup (filename);
for (root = root->children; root; root = root->next) {
if (strcmp((gchar *)root->name, "e-plugin") == 0) {
gchar *plugin_load_level, *is_system_plugin;
plugin_load_level = NULL;
plugin_load_level = e_plugin_xml_prop (root, "load_level");
if (plugin_load_level) {
if ((atoi (plugin_load_level) == load_level) ) {
ep = ep_load_plugin (root, pdoc);
if (ep && load_level == 1)
e_plugin_invoke (
ep, "load_plugin_type_register_function", NULL);
}
} else if (load_level == 2) {
ep = ep_load_plugin (root, pdoc);
}
if (ep) {
/* README: Maybe we can use load_levels to
* achieve the same thing. But it may be
* confusing for a plugin writer. */
is_system_plugin =
e_plugin_xml_prop (root, "system_plugin");
if (g_strcmp0 (is_system_plugin, "true") == 0) {
e_plugin_enable (ep, TRUE);
ep->flags |= E_PLUGIN_FLAGS_SYSTEM_PLUGIN;
} else
ep->flags &= ~E_PLUGIN_FLAGS_SYSTEM_PLUGIN;
g_free (is_system_plugin);
ep = NULL;
}
}
}
xmlFreeDoc (pdoc->doc);
g_free (pdoc->filename);
g_free (pdoc);
return 0;
}
static void
plugin_load_subclass (GType type,
GHashTable *hash_table)
{
EPluginClass *class;
class = g_type_class_ref (type);
g_hash_table_insert (hash_table, (gpointer) class->type, class);
}
static void
plugin_hook_load_subclass (GType type,
GHashTable *hash_table)
{
EPluginHookClass *hook_class;
EPluginHookClass *dupe_class;
gpointer key;
hook_class = g_type_class_ref (type);
/* Sanity check the hook class. */
if (hook_class->id == NULL || *hook_class->id == '\0') {
g_warning (
"%s has no hook ID, so skipping",
G_OBJECT_CLASS_NAME (hook_class));
g_type_class_unref (hook_class);
return;
}
/* Check for class ID collisions. */
dupe_class = g_hash_table_lookup (hash_table, hook_class->id);
if (dupe_class != NULL) {
g_warning (
"%s and %s have the same hook "
"ID ('%s'), so skipping %s",
G_OBJECT_CLASS_NAME (dupe_class),
G_OBJECT_CLASS_NAME (hook_class),
hook_class->id,
G_OBJECT_CLASS_NAME (hook_class));
g_type_class_unref (hook_class);
return;
}
key = (gpointer) hook_class->id;
g_hash_table_insert (hash_table, key, hook_class);
}
/**
* e_plugin_load_plugins:
*
* Scan the search path, looking for plugin definitions, and load them
* into memory.
*
* Return value: Returns -1 if an error occurred.
**/
gint
e_plugin_load_plugins (void)
{
GSettings *settings;
gchar **strv;
gint i;
if (eph_types != NULL)
return 0;
ep_types = g_hash_table_new (g_str_hash, g_str_equal);
eph_types = g_hash_table_new (g_str_hash, g_str_equal);
ep_plugins = g_hash_table_new (g_str_hash, g_str_equal);
/* We require that all GTypes for EPlugin and EPluginHook
* subclasses be registered prior to loading any plugins.
* It greatly simplifies the loading process. */
e_type_traverse (
E_TYPE_PLUGIN, (ETypeFunc)
plugin_load_subclass, ep_types);
e_type_traverse (
E_TYPE_PLUGIN_HOOK, (ETypeFunc)
plugin_hook_load_subclass, eph_types);
settings = g_settings_new ("org.gnome.evolution");
strv = g_settings_get_strv (settings, "disabled-eplugins");
for (i = 0, ep_disabled = NULL; strv[i] != NULL; i++)
ep_disabled = g_slist_append (ep_disabled, g_strdup (strv[i]));
g_strfreev (strv);
g_object_unref (settings);
for (i = 0; i < 3; i++) {
GDir *dir;
const gchar *d;
const gchar *path = EVOLUTION_PLUGINDIR;
pd(printf("scanning plugin dir '%s'\n", path));
dir = g_dir_open (path, 0, NULL);
if (dir == NULL) {
/*g_warning("Could not find plugin path: %s", path);*/
continue;
}
while ((d = g_dir_read_name (dir))) {
if (g_str_has_suffix (d, ".eplug")) {
gchar *name;
name = g_build_filename (path, d, NULL);
ep_load (name, i);
g_free (name);
}
}
g_dir_close (dir);
}
return 0;
}
static void
ep_list_plugin (gpointer key,
gpointer val,
gpointer dat)
{
GSList **l = (GSList **) dat;
*l = g_slist_prepend(*l, g_object_ref(val));
}
/**
* e_plugin_list_plugins: List all plugins.
*
* Static class method to retrieve a list of all current plugins. They
* are listed in no particular order.
*
* Return value: A GSList of all plugins, they must be
* g_object_unref'd and the list freed.
**/
GSList *
e_plugin_list_plugins (void)
{
GSList *l = NULL;
if (ep_plugins)
g_hash_table_foreach (ep_plugins, ep_list_plugin, &l);
return l;
}
/**
* e_plugin_construct:
* @ep: an #EPlugin
* @root: The XML root node of the sub-tree containing the plugin
* definition.
*
* Helper to invoke the construct virtual method.
*
* Return value: The return from the construct virtual method.
**/
gint
e_plugin_construct (EPlugin *ep,
xmlNodePtr root)
{
EPluginClass *class;
g_return_val_if_fail (E_IS_PLUGIN (ep), -1);
class = E_PLUGIN_GET_CLASS (ep);
g_return_val_if_fail (class->construct != NULL, -1);
return class->construct (ep, root);
}
/**
* e_plugin_invoke:
* @ep: an #EPlugin
* @name: The name of the function to invoke. The format of this name
* will depend on the EPlugin type and its language conventions.
* @data: The argument to the function. Its actual type depends on
* the hook on which the function resides. It is up to the called
* function to get this right.
*
* Helper to invoke the invoke virtual method.
*
* Return value: The return of the plugin invocation.
**/
gpointer
e_plugin_invoke (EPlugin *ep,
const gchar *name,
gpointer data)
{
EPluginClass *class;
g_return_val_if_fail (E_IS_PLUGIN (ep), NULL);
g_return_val_if_fail (name != NULL, NULL);
/* Prevent invocation on a disabled plugin. */
g_return_val_if_fail (ep->enabled, NULL);
class = E_PLUGIN_GET_CLASS (ep);
g_return_val_if_fail (class->invoke != NULL, NULL);
return class->invoke (ep, name, data);
}
/**
* e_plugin_get_symbol:
* @ep: an #EPlugin
* @name: The name of the symbol to fetch. The format of this name
* will depend on the EPlugin type and its language conventions.
*
* Helper to fetch a symbol name from a plugin.
*
* Return value: the symbol value, or %NULL if not found
**/
gpointer
e_plugin_get_symbol (EPlugin *ep,
const gchar *name)
{
EPluginClass *class;
g_return_val_if_fail (E_IS_PLUGIN (ep), NULL);
class = E_PLUGIN_GET_CLASS (ep);
g_return_val_if_fail (class->get_symbol != NULL, NULL);
return class->get_symbol (ep, name);
}
/**
* e_plugin_enable:
* @ep: an #EPlugin
* @state: %TRUE to enable, %FALSE to disable
*
* Set the enable state of a plugin.
*
* THIS IS NOT FULLY IMPLEMENTED YET
**/
void
e_plugin_enable (EPlugin *ep,
gint state)
{
EPluginClass *class;
g_return_if_fail (E_IS_PLUGIN (ep));
if ((ep->enabled == 0) == (state == 0))
return;
class = E_PLUGIN_GET_CLASS (ep);
g_return_if_fail (class->enable != NULL);
class->enable (ep, state);
g_object_notify (G_OBJECT (ep), "enabled");
}
/**
* e_plugin_get_configure_widget
* @ep: an #EPlugin
*
* Plugin itself should have implemented "e_plugin_lib_get_configure_widget"
* function * of prototype EPluginLibGetConfigureWidgetFunc.
*
* Returns: Configure widget or %NULL
**/
GtkWidget *
e_plugin_get_configure_widget (EPlugin *ep)
{
EPluginClass *class;
g_return_val_if_fail (E_IS_PLUGIN (ep), NULL);
class = E_PLUGIN_GET_CLASS (ep);
if (class->get_configure_widget == NULL)
return NULL;
return class->get_configure_widget (ep);
}
/**
* e_plugin_xml_prop:
* @node: An XML node.
* @id: The name of the property to retrieve.
*
* A static helper function to look up a property on an XML node, and
* ensure it is allocated in GLib system memory.
*
* Return value: The property, allocated in GLib memory, or NULL if no
* such property exists.
**/
gchar *
e_plugin_xml_prop (xmlNodePtr node,
const gchar *id)
{
xmlChar *xml_prop;
gchar *glib_prop = NULL;
xml_prop = xmlGetProp (node, (xmlChar *) id);
if (xml_prop != NULL) {
glib_prop = g_strdup ((gchar *) xml_prop);
xmlFree (xml_prop);
}
return glib_prop;
}
/**
* e_plugin_xml_prop_domain:
* @node: An XML node.
* @id: The name of the property to retrieve.
* @domain: The translation domain for this string.
*
* A static helper function to look up a property on an XML node, and
* translate it based on @domain.
*
* Return value: The property, allocated in GLib memory, or NULL if no
* such property exists.
**/
gchar *
e_plugin_xml_prop_domain (xmlNodePtr node,
const gchar *id,
const gchar *domain)
{
gchar *p, *out;
p = (gchar *) xmlGetProp (node, (const guchar *) id);
if (p == NULL)
return NULL;
out = g_strdup (dgettext (domain, p));
xmlFree (p);
return out;
}
/**
* e_plugin_xml_int:
* @node: An XML node.
* @id: The name of the property to retrieve.
* @def: A default value if the property doesn't exist. Can be used
* to determine if the property isn't set.
*
* A static helper function to look up a property on an XML node as an
* integer. If the property doesn't exist, then @def is returned as a
* default value instead.
*
* Return value: The value if set, or @def if not.
**/
gint
e_plugin_xml_int (xmlNodePtr node,
const gchar *id,
gint def)
{
gchar *p = (gchar *) xmlGetProp (node, (const guchar *) id);
if (p)
return atoi (p);
else
return def;
}
/**
* e_plugin_xml_content:
* @node:
*
* A static helper function to retrieve the entire textual content of
* an XML node, and ensure it is allocated in GLib system memory. If
* GLib isn't using the system malloc them it must copy the content.
*
* Return value: The node content, allocated in GLib memory.
**/
gchar *
e_plugin_xml_content (xmlNodePtr node)
{
gchar *p = (gchar *) xmlNodeGetContent (node);
if (g_mem_is_system_malloc ()) {
return p;
} else {
gchar * out = g_strdup (p);
if (p)
xmlFree (p);
return out;
}
}
/**
* e_plugin_xml_content_domain:
* @node:
* @domain:
*
* A static helper function to retrieve the entire textual content of
* an XML node, and ensure it is allocated in GLib system memory. If
* GLib isn't using the system malloc them it must copy the content.
*
* Return value: The node content, allocated in GLib memory.
**/
gchar *
e_plugin_xml_content_domain (xmlNodePtr node,
const gchar *domain)
{
gchar *p, *out;
p = (gchar *) xmlNodeGetContent (node);
if (p == NULL)
return NULL;
out = g_strdup (dgettext (domain, p));
xmlFree (p);
return out;
}
/* ********************************************************************** */
G_DEFINE_TYPE (
EPluginHook,
e_plugin_hook,
G_TYPE_OBJECT)
static gint
eph_construct (EPluginHook *eph,
EPlugin *ep,
xmlNodePtr root)
{
eph->plugin = ep;
return 0;
}
static void
eph_enable (EPluginHook *eph,
gint state)
{
/* NOOP */
}
static void
e_plugin_hook_class_init (EPluginHookClass *class)
{
class->construct = eph_construct;
class->enable = eph_enable;
}
static void
e_plugin_hook_init (EPluginHook *hook)
{
}
/**
* e_plugin_hook_enable: Set hook enabled state.
* @eph:
* @state:
*
* Set the enabled state of the plugin hook. This is called by the
* plugin code.
*
* THIS IS NOT FULY IMEPLEMENTED YET
**/
void
e_plugin_hook_enable (EPluginHook *eph,
gint state)
{
EPluginHookClass *class;
g_return_if_fail (E_IS_PLUGIN_HOOK (eph));
class = E_PLUGIN_HOOK_GET_CLASS (eph);
g_return_if_fail (class->enable != NULL);
class->enable (eph, state);
}
/**
* e_plugin_hook_mask:
* @root: An XML node.
* @map: A zero-fill terminated array of EPluginHookTargeKeys used to
* map a string with a bit value.
* @prop: The property name.
*
* This is a static helper function which looks up a property @prop on
* the XML node @root, and then uses the @map table to convert it into
* a bitmask. The property value is a comma separated list of
* enumeration strings which are indexed into the @map table.
*
* Return value: A bitmask representing the inclusive-or of all of the
* integer values of the corresponding string id's stored in the @map.
**/
guint32
e_plugin_hook_mask (xmlNodePtr root,
const EPluginHookTargetKey *map,
const gchar *prop)
{
gchar *val, *p, *start, c;
guint32 mask = 0;
val = (gchar *) xmlGetProp (root, (const guchar *) prop);
if (val == NULL)
return 0;
p = val;
do {
start = p;
while (*p && *p != ',')
p++;
c = *p;
*p = 0;
if (start != p) {
gint i;
for (i = 0; map[i].key; i++) {
if (!strcmp (map[i].key, start)) {
mask |= map[i].value;
break;
}
}
}
*p++ = c;
} while (c);
xmlFree (val);
return mask;
}
/**
* e_plugin_hook_id:
* @root:
* @map:
* @prop:
*
* This is a static helper function which looks up a property @prop on
* the XML node @root, and then uses the @map table to convert it into
* an integer.
*
* This is used as a helper wherever you need to represent an
* enumerated value in the XML.
*
* Return value: If the @prop value is in @map, then the corresponding
* integer value, if not, then ~0.
**/
guint32
e_plugin_hook_id (xmlNodePtr root,
const EPluginHookTargetKey *map,
const gchar *prop)
{
gchar *val;
gint i;
val = (gchar *) xmlGetProp (root, (const guchar *) prop);
if (val == NULL)
return ~0;
for (i = 0; map[i].key; i++) {
if (!strcmp (map[i].key, val)) {
xmlFree (val);
return map[i].value;
}
}
xmlFree (val);
return ~0;
}