/* 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 and * Havoc Pennington . * * 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 #include "guppi-plug-in-spec.h" #include #include #include #include #include #include #include #include #include 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 (" \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 $ */