/* This is -*- C -*- */
/* $Id: guppi-plug-in-spec.c,v 1.19 2002/01/19 02:45:26 trow Exp $ */
/*
* guppi-plug-in-spec.c
*
* Copyright (C) 2000 EMC Capital Management, Inc.
*
* Developed by Jon Trowbridge <trow@gnu.org> and
* Havoc Pennington <hp@pobox.com>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*/
#include <config.h>
#include "guppi-plug-in-spec.h"
#include <stdio.h>
#include <dirent.h>
#include <ctype.h>
#include <string.h>
#include <glib.h>
#include <libgnome/gnome-defs.h>
#include <libgnome/gnome-util.h>
#include <libgnome/gnome-i18n.h>
#include <guppi-useful.h>
static GtkObjectClass *parent_class = NULL;
static void
guppi_plug_in_spec_finalize (GtkObject * obj)
{
GuppiPlugInSpec *spec = GUPPI_PLUG_IN_SPEC (obj);
guppi_finalized (obj);
guppi_free0 (spec->path);
guppi_free0 (spec->so_filename);
guppi_free (spec->code);
guppi_free (spec->type);
guppi_free (spec->name);
guppi_strfreev (spec->copyright_vec);
guppi_strfreev (spec->author_vec);
guppi_strfreev (spec->keyword_vec);
guppi_strfreev (spec->depends_vec);
guppi_strfreev (spec->provides_vec);
guppi_strfreev (spec->exported_symbol_vec);
guppi_free (spec->preloaded_scm_filename);
guppi_free (spec->preloaded_py_filename);
guppi_free (spec->so_filename);
guppi_free (spec->icon);
guppi_unref0 (spec->plug_in);
if (parent_class->finalize)
parent_class->finalize (obj);
}
static void
guppi_plug_in_spec_class_init (GuppiPlugInSpecClass * klass)
{
GtkObjectClass *object_class = (GtkObjectClass *) klass;
parent_class = gtk_type_class (GTK_TYPE_OBJECT);
object_class->finalize = guppi_plug_in_spec_finalize;
}
static void
guppi_plug_in_spec_init (GuppiPlugInSpec * obj)
{
}
GtkType
guppi_plug_in_spec_get_type (void)
{
static GtkType guppi_plug_in_spec_type = 0;
if (!guppi_plug_in_spec_type) {
static const GtkTypeInfo guppi_plug_in_spec_info = {
"GuppiPlugInSpec",
sizeof (GuppiPlugInSpec),
sizeof (GuppiPlugInSpecClass),
(GtkClassInitFunc) guppi_plug_in_spec_class_init,
(GtkObjectInitFunc) guppi_plug_in_spec_init,
NULL, NULL, (GtkClassInitFunc) NULL
};
guppi_plug_in_spec_type =
gtk_type_unique (GTK_TYPE_OBJECT, &guppi_plug_in_spec_info);
}
return guppi_plug_in_spec_type;
}
static gchar **
list2vec (GList * lst)
{
gchar **vec = NULL;
gint i = 0;
vec = guppi_new0 (gchar *, g_list_length (lst) + 1);
while (lst != NULL) {
vec[i] = (gchar *) lst->data;
lst->data = NULL;
lst = g_list_next (lst);
++i;
}
g_list_free (lst);
return vec;
}
static gboolean
parse_version (const gchar * txt, gint * major, gint * minor, gint * micro)
{
if (sscanf (txt, "%d.%d.%d", major, minor, micro) == 3)
return TRUE;
else if (sscanf (txt, "%d.%d", major, minor) == 2) {
*micro = 0;
return TRUE;
}
return FALSE;
}
static gchar *
plug_in_filename (const gchar * path, const gchar * name)
{
gchar *str;
gchar *path2;
str = g_concat_dir_and_file (path, name);
guppi_outside_alloc (str);
if (g_file_exists (str))
return str;
guppi_free (str);
path2 = guppi_strdup_printf ("%s%c.libs", path, G_DIR_SEPARATOR);
str = g_concat_dir_and_file (path2, name);
guppi_outside_alloc (str);
guppi_free (path2);
if (g_file_exists (str))
return str;
guppi_free (str);
return NULL;
}
enum {
SPEC_CODE,
SPEC_TYPE,
SPEC_NAME,
SPEC_COMMENT,
SPEC_VERSION,
SPEC_COPYRIGHT,
SPEC_AUTHOR,
SPEC_KEYWORD,
SPEC_DEPENDS,
SPEC_PROVIDES,
SPEC_EXPORTED_SYMBOL,
SPEC_PRELOADED_SCM,
SPEC_PRELOADED_PYTHON,
SPEC_PLUGIN,
SPEC_ICON,
LAST_SPEC
};
const gchar *field_name[LAST_SPEC] = {
"Code",
"Type",
"Name",
"Comment",
"Version",
"Copyright",
"Author",
"Keyword",
"Depends",
"Provides",
"ExportedSymbol",
"PreloadedScheme",
"PreloadedPython",
"PlugIn",
"Icon"
};
static gboolean
parse_spec_file (GuppiPlugInSpec * spec, const gchar * filename)
{
FILE *in;
gchar buffer[512];
GList *copyright_list = NULL;
GList *author_list = NULL;
GList *keyword_list = NULL;
GList *depends_list = NULL;
GList *provides_list = NULL;
GList *export_symb_list = NULL;
gchar *lingua = NULL;
/*
* Lingua currently never gets set to anything non-NULL.
* This obviously needs to be fixed.
*/
in = fopen (filename, "r");
if (in == NULL)
return FALSE;
spec->path = g_dirname (filename);
guppi_outside_alloc (spec->path);
if (!fgets (buffer, 512, in)) {
/* empty file? */
fclose (in);
return FALSE;
}
if (!g_strcasecmp (buffer, "[Guppi Plug-in]")) {
fclose (in);
return FALSE;
}
while (fgets (buffer, 512, in)) {
gchar *p;
gchar *q;
gchar *key;
gchar *value;
gchar *line_lingua;
gint type;
gboolean supports_lingua;
g_strstrip (buffer);
/* I know, I know... this is almost as bad a using "goto" */
if (buffer[0] == '\0')
continue;
p = strchr (buffer, '=');
if (p == NULL) {
g_warning ("Bad line: %s", buffer);
fclose (in);
return FALSE;
}
*p = '\0';
value = p + 1;
key = guppi_strdup (buffer);
line_lingua = NULL;
p = strrchr (key, '[');
q = strrchr (key, ']');
if (p != NULL && q != NULL && p < q) {
*p = '\0';
*q = '\0';
line_lingua = p + 1;
}
for (type = 0; type < LAST_SPEC && g_strcasecmp (key, field_name[type]);
++type);
if (type == LAST_SPEC) {
g_warning ("Bad key: %s", key);
fclose (in);
return FALSE;
}
supports_lingua = (type == SPEC_NAME ||
type == SPEC_COMMENT || type == SPEC_KEYWORD);
if (line_lingua && !supports_lingua) {
g_warning ("Ignoring language tag [%s] on key \"%s\"", line_lingua,
key);
guppi_free (line_lingua);
line_lingua = NULL;
}
if (!supports_lingua ||
((line_lingua == NULL && lingua == NULL) ||
(line_lingua != NULL && lingua != NULL &&
!g_strcasecmp (line_lingua, lingua)))) {
switch (type) {
case SPEC_CODE:
spec->code = guppi_strdup (value);
break;
case SPEC_TYPE:
spec->type = guppi_strdup (value);
break;
case SPEC_NAME:
spec->name = guppi_strdup (value);
break;
case SPEC_COMMENT:
spec->comment = guppi_strdup (value);
break;
case SPEC_VERSION:
g_assert (parse_version (value,
&spec->major_version,
&spec->minor_version, &spec->micro_version));
break;
case SPEC_COPYRIGHT:
copyright_list = g_list_append (copyright_list, guppi_strdup (value));
break;
case SPEC_AUTHOR:
author_list = g_list_append (author_list, guppi_strdup (value));
break;
case SPEC_KEYWORD:
keyword_list = g_list_append (keyword_list, guppi_strdup (value));
break;
case SPEC_DEPENDS:
depends_list = g_list_append (keyword_list, guppi_strdup (value));
break;
case SPEC_PROVIDES:
provides_list = g_list_append (provides_list, guppi_strdup (value));
break;
case SPEC_EXPORTED_SYMBOL:
export_symb_list = g_list_append (export_symb_list, guppi_strdup (value));
break;
case SPEC_PRELOADED_SCM:
spec->preloaded_scm_filename = guppi_strdup (value);
break;
case SPEC_PRELOADED_PYTHON:
spec->preloaded_py_filename = guppi_strdup (value);
break;
case SPEC_PLUGIN:
spec->so_filename = guppi_strdup (value);
break;
case SPEC_ICON:
spec->icon = guppi_strdup (value);
break;
default:
g_assert_not_reached ();
}
}
guppi_free (key);
}
spec->copyright_vec = list2vec (copyright_list);
spec->author_vec = list2vec (author_list);
spec->keyword_vec = list2vec (keyword_list);
spec->depends_vec = list2vec (depends_list);
spec->provides_vec = list2vec (provides_list);
spec->exported_symbol_vec = list2vec (export_symb_list);
fclose (in);
return TRUE;
}
static gboolean
validate_spec (GuppiPlugInSpec * spec)
{
gchar *s;
if (spec->code == NULL) {
g_print ("No code! (%s)\n", spec->path);
return FALSE;
}
if (spec->type == NULL) {
g_print ("No type! (%s)\n", spec->path);
return FALSE;
}
if (spec->name == NULL) {
g_print ("No name! (%s)\n", spec->path);
return FALSE;
}
/* Make sure the plug-in file exists. */
if (spec->so_filename == NULL) {
g_print ("No plug-in specified (%s)\n", spec->path);
return FALSE;
}
s = plug_in_filename (spec->path, spec->so_filename);
if (s == NULL) {
g_print ("Plug-in %s not found\n", spec->so_filename);
return FALSE;
}
guppi_free (s);
return TRUE;
}
static void
process_spec_exported_symbols (GuppiPlugInSpec *spec)
{
if (!spec->exported_symbol_vec)
return;
#if 0
for (i=0; spec->exported_symbol_vec[i]; ++i) {
if (guppi_supports_guile () && guppi_guile_is_active ()) {
guppi_scm_define_autoloaded_symbol (spec->exported_symbol_vec[i],
spec->type, spec->code);
}
}
#endif
}
static void
do_spec_preloads (GuppiPlugInSpec * spec)
{
guppi_pixmap_path_add (spec->path);
/* guppi_script_path_add (spec->path); */
guppi_glade_path_add (spec->path);
#if 0
if (spec->preloaded_scm_filename && guppi_supports_guile ()) {
gchar *path = g_concat_dir_and_file (spec->path,
spec->preloaded_scm_filename);
guppi_outside_alloc (path);
if (g_file_exists (path) && guppi_file_is_guile_script (path))
guppi_execute_script (path);
else
g_warning ("Couldn't preload \"%s\" as scheme", path);
guppi_free (path);
}
if (spec->preloaded_py_filename && guppi_supports_python ()) {
gchar *path = g_concat_dir_and_file (spec->path,
spec->preloaded_py_filename);
guppi_outside_alloc (path);
if (g_file_exists (path) && guppi_file_is_python_script (path))
guppi_execute_script (path);
else
g_warning ("Couldn't preload \"%s\" as python", path);
guppi_free (path);
}
#endif
}
GuppiPlugInSpec *
guppi_plug_in_spec_new (const gchar * filename)
{
GuppiPlugInSpec *spec;
g_return_val_if_fail (filename != NULL, NULL);
spec = GUPPI_PLUG_IN_SPEC (guppi_type_new (guppi_plug_in_spec_get_type ()));
if (!parse_spec_file (spec, filename) || !validate_spec (spec)) {
guppi_unref (spec);
return NULL;
}
process_spec_exported_symbols (spec);
do_spec_preloads (spec);
return spec;
}
GuppiPlugIn *
guppi_plug_in_spec_plug_in (GuppiPlugInSpec * spec)
{
g_return_val_if_fail (spec != NULL && GUPPI_IS_PLUG_IN_SPEC (spec), NULL);
if (spec->plug_in == NULL) {
gchar *path;
path = plug_in_filename (spec->path, spec->so_filename);
if (path == NULL) {
g_print ("Plug-in %s (%s) not found.\n", spec->so_filename, spec->code);
return NULL;
}
spec->plug_in = guppi_plug_in_load (path);
spec->plug_in->spec = spec;
guppi_free (path);
}
return spec->plug_in;
}
/**********************************************************************/
static GHashTable *plug_in_table = NULL;
static gint
version_compare (const GuppiPlugInSpec * a, const GuppiPlugInSpec * b)
{
if (a->major_version < b->major_version)
return -1;
else if (a->major_version > b->major_version)
return +1;
else if (a->minor_version < b->minor_version)
return -1;
else if (a->minor_version > b->minor_version)
return +1;
else if (a->micro_version < b->micro_version)
return -1;
else if (a->micro_version > b->micro_version)
return +1;
else
return 0;
}
static void
register_plug_in (GuppiPlugInSpec * info)
{
gpointer data;
GuppiPlugInSpec *ai;
GHashTable *type_table;
gint rv;
g_return_if_fail (info != NULL);
g_return_if_fail (info->type != NULL);
g_return_if_fail (info->code != NULL);
g_return_if_fail (info->name != NULL);
if (plug_in_table == NULL)
plug_in_table = g_hash_table_new (g_str_hash, g_str_equal);
type_table = (GHashTable *) g_hash_table_lookup (plug_in_table, info->type);
if (type_table == NULL) {
type_table = g_hash_table_new (g_str_hash, g_str_equal);
g_hash_table_insert (plug_in_table, (gchar *) info->type, type_table);
}
data = g_hash_table_lookup (type_table, info->code);
if (data != NULL) {
ai = GUPPI_PLUG_IN_SPEC (data);
g_assert (ai != NULL);
rv = version_compare (ai, info);
if (rv == -1) {
/* The already-loaded version is older */
g_message ("Replacing %s %d.%d.%d with %d.%d.%d",
info->name,
info->major_version, info->minor_version,
info->micro_version, ai->major_version, ai->minor_version,
ai->micro_version);
g_hash_table_remove (type_table, info->code);
} else {
/* The already-loaded version is newer or the same */
g_message ("Skipping %s %d.%d.%d",
info->name,
info->major_version, info->minor_version,
info->micro_version);
return;
}
}
g_hash_table_insert (type_table, (gchar *) info->code, info);
}
GuppiPlugInSpec *
guppi_plug_in_spec_lookup (const gchar * type, const gchar * code)
{
gpointer data;
g_return_val_if_fail (type != NULL, NULL);
g_return_val_if_fail (code != NULL, NULL);
if (plug_in_table == NULL)
return NULL;
data = g_hash_table_lookup (plug_in_table, type);
if (data == NULL)
return NULL;
data = g_hash_table_lookup ((GHashTable *) data, code);
if (data == NULL)
return NULL;
return GUPPI_PLUG_IN_SPEC (data);
}
GuppiPlugIn *
guppi_plug_in_lookup (const gchar * type, const gchar * code)
{
GuppiPlugInSpec *spec;
g_return_val_if_fail (type != NULL, NULL);
g_return_val_if_fail (code != NULL, NULL);
spec = guppi_plug_in_spec_lookup (type, code);
if (spec == NULL)
return NULL;
return guppi_plug_in_spec_plug_in (spec);
}
/***********************************************************************/
gboolean
guppi_plug_in_exists (const gchar * type, const gchar * code)
{
g_return_val_if_fail (type != NULL && code != NULL, FALSE);
return guppi_plug_in_spec_lookup (type, code) != NULL;
}
gboolean
guppi_plug_in_is_loaded (const gchar * type, const gchar * code)
{
GuppiPlugInSpec *spec;
g_return_val_if_fail (type != NULL && code != NULL, FALSE);
spec = guppi_plug_in_spec_lookup (type, code);
return spec && spec->plug_in;
}
void
guppi_plug_in_force_load (const gchar * type, const gchar * code)
{
g_return_if_fail (type != NULL && code != NULL);
guppi_plug_in_lookup (type, code);
}
/***********************************************************************/
static GList *plug_in_dirs = NULL;
static void
guppi_plug_in_path_clear (void)
{
GList *iter = plug_in_dirs;
while (iter != NULL) {
guppi_free (iter->data);
iter = g_list_next (iter);
}
g_list_free (plug_in_dirs);
plug_in_dirs = NULL;
}
static GList *
guppi_plug_in_path_split (const gchar * path)
{
const gchar *start;
const gchar *curr;
GList *pathlist = NULL;
if (path == NULL)
return NULL;
start = path;
while (*start != '\0') {
while (*start == ':')
++start;
curr = start;
while (*curr != ':' && *curr != '\0')
++curr;
if (start != curr)
pathlist = g_list_append (pathlist, guppi_strndup (start, curr - start));
start = curr;
}
return pathlist;
}
void
guppi_plug_in_path_set (const gchar * path)
{
g_return_if_fail (path != NULL);
guppi_plug_in_path_clear ();
plug_in_dirs = guppi_plug_in_path_split (path);
}
void
guppi_plug_in_path_prepend (const gchar * path)
{
g_return_if_fail (path != NULL);
plug_in_dirs =
g_list_concat (guppi_plug_in_path_split (path), plug_in_dirs);
}
void
guppi_plug_in_path_append (const gchar * path)
{
g_return_if_fail (path != NULL);
plug_in_dirs =
g_list_concat (plug_in_dirs, guppi_plug_in_path_split (path));
}
void
guppi_plug_in_path_reset_to_default (void)
{
gchar *env;
guppi_plug_in_path_clear ();
if (development_path_hacks ())
guppi_plug_in_path_append ("../../plug-ins");
env = getenv ("GUPPI_PLUGIN_PATH");
if (env)
guppi_plug_in_path_append (env);
#ifdef GUPPI_PLUGINS
if (!development_path_hacks ())
guppi_plug_in_path_append (GUPPI_PLUGINS);
#endif
}
void
guppi_plug_in_path_dump (void)
{
GList *iter = plug_in_dirs;
g_print (_("Plug-in Search Path:"));
g_print ("\n");
if (iter == NULL)
g_print (" <none>\n");
while (iter != NULL) {
g_print (" %s\n", (gchar *) iter->data);
iter = g_list_next (iter);
}
g_print ("\n");
}
/***********************************************************************/
void
guppi_plug_in_spec_find (const gchar * path, gboolean recurse)
{
DIR *dir;
struct dirent *dirent;
g_return_if_fail (path != NULL);
if (!g_file_test (path, G_FILE_TEST_ISDIR))
return;
dir = opendir (path);
if (dir == NULL) {
g_message ("couldn't open %s", path);
return;
}
while ((dirent = readdir (dir)) != NULL) {
gchar *full_name;
full_name = g_concat_dir_and_file (path, dirent->d_name);
if (recurse &&
strcmp (dirent->d_name, ".") &&
strcmp (dirent->d_name, "..") &&
strcmp (dirent->d_name, "CVS") &&
strcmp (dirent->d_name, ".deps") &&
strcmp (dirent->d_name, ".libs") &&
g_file_test (full_name, G_FILE_TEST_ISDIR)) {
guppi_plug_in_spec_find (full_name, recurse);
} else if (g_file_test (full_name, G_FILE_TEST_ISFILE) &&
!strcmp (g_extension_pointer (dirent->d_name), "plugin")) {
GuppiPlugInSpec *spec;
spec = guppi_plug_in_spec_new (full_name);
if (spec != NULL)
register_plug_in (spec);
}
g_free (full_name);
}
closedir (dir);
}
void
guppi_plug_in_spec_find_all (void)
{
GList *i = plug_in_dirs;
while (i != NULL) {
guppi_plug_in_spec_find ((gchar *) (i->data), TRUE);
i = g_list_next (i);
}
}
void
guppi_plug_in_load_all (void)
{
guppi_plug_in_spec_find_all ();
}
static void
load_by_type_fn (GuppiPlugInSpec * spec, gpointer ptr)
{
guppi_plug_in_spec_plug_in (spec);
}
void
guppi_plug_in_spec_load_by_type (const gchar * type)
{
g_return_if_fail (type != NULL);
guppi_plug_in_spec_foreach_of_type (type, load_by_type_fn, NULL);
}
/***********************************************************************/
struct foreach_info {
GuppiPlugInSpecFn func;
gpointer data;
};
static void
hfunc_inner (gpointer key, gpointer val, gpointer user_data)
{
struct foreach_info *fi = (struct foreach_info *) user_data;
g_return_if_fail (GUPPI_IS_PLUG_IN_SPEC (val));
(fi->func) (GUPPI_PLUG_IN_SPEC (val), fi->data);
}
static void
hfunc_outer (gpointer key, gpointer val, gpointer user_data)
{
g_hash_table_foreach ((GHashTable *) val, hfunc_inner, user_data);
}
void
guppi_plug_in_spec_foreach (GuppiPlugInSpecFn func, gpointer data)
{
struct foreach_info fi;
fi.func = func;
fi.data = data;
g_return_if_fail (func != NULL);
if (plug_in_table == NULL) /* no plug-ins available */
return;
g_hash_table_foreach (plug_in_table, hfunc_outer, &fi);
}
void
guppi_plug_in_spec_foreach_of_type (const gchar * type,
GuppiPlugInSpecFn func, gpointer data)
{
GHashTable *subtable;
struct foreach_info fi;
fi.func = func;
fi.data = data;
g_return_if_fail (type != NULL);
g_return_if_fail (func != NULL);
if (plug_in_table == NULL) /* no plug-ins available */
return;
subtable = (GHashTable *) g_hash_table_lookup (plug_in_table, type);
if (subtable != NULL)
g_hash_table_foreach (subtable, hfunc_inner, &fi);
}
static void
incr (GuppiPlugInSpec * foo, gpointer x)
{
++*(gint *) x;
}
gint
guppi_plug_in_count (void)
{
gint x = 0;
guppi_plug_in_spec_foreach (incr, &x);
return x;
}
gint
guppi_plug_in_count_by_type (const gchar * type)
{
gint x = 0;
g_return_val_if_fail (type != NULL, 0);
guppi_plug_in_spec_foreach_of_type (type, incr, &x);
return x;
}
static void
shutdown_iter_fn (gpointer key, gpointer val, gpointer data)
{
guppi_unref (val);
}
static void
shutdown_iter_fn_outer (gpointer key, gpointer val, gpointer data)
{
g_hash_table_foreach ((GHashTable *) val, shutdown_iter_fn, NULL);
g_hash_table_destroy ((GHashTable *) val);
}
void
guppi_plug_in_spec_shutdown (gpointer ignored)
{
guppi_plug_in_path_clear ();
if (plug_in_table != NULL) {
g_hash_table_foreach (plug_in_table, shutdown_iter_fn_outer, NULL);
g_hash_table_destroy (plug_in_table);
plug_in_table = NULL;
}
}
/* $Id: guppi-plug-in-spec.c,v 1.19 2002/01/19 02:45:26 trow Exp $ */
syntax highlighted by Code2HTML, v. 0.9.1