From 9706fce0a35405da54da4f8267e65e8d093fa27b Mon Sep 17 00:00:00 2001 From: Manish Singh Date: Mon, 21 Feb 2005 02:56:29 +0000 Subject: [PATCH] Support for custom plug-in interpreters, independent of OS support. 2005-02-20 Manish Singh Support for custom plug-in interpreters, independent of OS support. * app/core/Makefile.am * app/core/core-types.h * app/core/gimpinterpreterdb.[ch]: implemented GimpInterpreterDB, which handles registering and resolving custom plug-in interpreters. * app/core/gimp.[ch]: keep a GimpInterpreterDB around. * app/config/gimpcoreconfig.[ch] * app/config/gimprc-blurbs.h * app/dialogs/preferences-dialog.c * app/dialogs/user-install-dialog.c * app/widgets/gimphelp-ids.h: interpreter-path config stuff. * app/plug-in/plug-in.c: use registered interpreters when running plug-ins. * themes/Default/images/preferences/Makefile.am * themes/Default/images/preferences/folders-interp.png: just copied folders-plug-ins.png here, need a better one. * data/interpreters/Makefile.am: creates system interpreter directory. * data/interpreters/default.interp: sample interpreter file info. * data/Makefile.am * configure.in: add data/interpreters directory. * plug-ins/pygimp/Makefile.am: install pygimp.interp, which configures the python interpreter to point to the python we were built with. Also register the .py extension. * etc/gimprc * docs/gimprc.5.in: regenerated --- ChangeLog | 38 + app/config/gimpcoreconfig.c | 14 + app/config/gimpcoreconfig.h | 1 + app/config/gimprc-blurbs.h | 3 + app/core/Makefile.am | 2 + app/core/core-types.h | 1 + app/core/gimp.c | 14 + app/core/gimp.h | 1 + app/core/gimpinterpreterdb.c | 767 ++++++++++++++++++ app/core/gimpinterpreterdb.h | 68 ++ app/dialogs/preferences-dialog.c | 4 + app/dialogs/user-install-dialog.c | 10 + app/plug-in/gimpinterpreterdb.c | 767 ++++++++++++++++++ app/plug-in/gimpinterpreterdb.h | 68 ++ app/plug-in/gimpplugin.c | 32 +- app/plug-in/plug-in.c | 32 +- app/widgets/gimphelp-ids.h | 1 + configure.in | 1 + data/Makefile.am | 2 +- data/interpreters/.cvsignore | 2 + data/interpreters/Makefile.am | 7 + data/interpreters/default.interp | 15 + docs/gimprc.5.in | 28 +- etc/gimprc | 26 +- themes/Default/images/preferences/Makefile.am | 3 +- .../images/preferences/folders-interp.png | Bin 0 -> 3709 bytes 26 files changed, 1875 insertions(+), 32 deletions(-) create mode 100644 app/core/gimpinterpreterdb.c create mode 100644 app/core/gimpinterpreterdb.h create mode 100644 app/plug-in/gimpinterpreterdb.c create mode 100644 app/plug-in/gimpinterpreterdb.h create mode 100644 data/interpreters/.cvsignore create mode 100644 data/interpreters/Makefile.am create mode 100644 data/interpreters/default.interp create mode 100644 themes/Default/images/preferences/folders-interp.png diff --git a/ChangeLog b/ChangeLog index 5e8773b00e..d9bc0c5aa4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,41 @@ +2005-02-20 Manish Singh + + Support for custom plug-in interpreters, independent of OS support. + + * app/core/Makefile.am + * app/core/core-types.h + * app/core/gimpinterpreterdb.[ch]: implemented GimpInterpreterDB, + which handles registering and resolving custom plug-in interpreters. + + * app/core/gimp.[ch]: keep a GimpInterpreterDB around. + + * app/config/gimpcoreconfig.[ch] + * app/config/gimprc-blurbs.h + * app/dialogs/preferences-dialog.c + * app/dialogs/user-install-dialog.c + * app/widgets/gimphelp-ids.h: interpreter-path config stuff. + + * app/plug-in/plug-in.c: use registered interpreters when running + plug-ins. + + * themes/Default/images/preferences/Makefile.am + * themes/Default/images/preferences/folders-interp.png: just copied + folders-plug-ins.png here, need a better one. + + * data/interpreters/Makefile.am: creates system interpreter directory. + + * data/interpreters/default.interp: sample interpreter file info. + + * data/Makefile.am + * configure.in: add data/interpreters directory. + + * plug-ins/pygimp/Makefile.am: install pygimp.interp, which configures + the python interpreter to point to the python we were built with. Also + register the .py extension. + + * etc/gimprc + * docs/gimprc.5.in: regenerated + 2005-02-20 Jay Cox * plug-ins/common/psd.c: Fix layer mask support. Addresses bug diff --git a/app/config/gimpcoreconfig.c b/app/config/gimpcoreconfig.c index 9cb4d022bd..830d142c04 100644 --- a/app/config/gimpcoreconfig.c +++ b/app/config/gimpcoreconfig.c @@ -73,6 +73,7 @@ enum PROP_INTERPOLATION_TYPE, PROP_PLUG_IN_PATH, PROP_MODULE_PATH, + PROP_INTERPRETER_PATH, PROP_ENVIRON_PATH, PROP_BRUSH_PATH, PROP_BRUSH_PATH_WRITABLE, @@ -169,6 +170,11 @@ gimp_core_config_class_init (GimpCoreConfigClass *klass) GIMP_CONFIG_PATH_DIR_LIST, gimp_config_build_plug_in_path ("modules"), GIMP_CONFIG_PARAM_RESTART); + GIMP_CONFIG_INSTALL_PROP_PATH (object_class, PROP_INTERPRETER_PATH, + "interpreter-path", INTERPRETER_PATH_BLURB, + GIMP_CONFIG_PATH_DIR_LIST, + gimp_config_build_plug_in_path ("interpreters"), + GIMP_CONFIG_PARAM_RESTART); GIMP_CONFIG_INSTALL_PROP_PATH (object_class, PROP_ENVIRON_PATH, "environ-path", ENVIRON_PATH_BLURB, GIMP_CONFIG_PATH_DIR_LIST, @@ -357,6 +363,7 @@ gimp_core_config_finalize (GObject *object) g_free (core_config->plug_in_path); g_free (core_config->module_path); + g_free (core_config->interpreter_path); g_free (core_config->environ_path); g_free (core_config->brush_path); g_free (core_config->brush_path_writable); @@ -408,6 +415,10 @@ gimp_core_config_set_property (GObject *object, g_free (core_config->module_path); core_config->module_path = g_value_dup_string (value); break; + case PROP_INTERPRETER_PATH: + g_free (core_config->interpreter_path); + core_config->interpreter_path = g_value_dup_string (value); + break; case PROP_ENVIRON_PATH: g_free (core_config->environ_path); core_config->environ_path = g_value_dup_string (value); @@ -559,6 +570,9 @@ gimp_core_config_get_property (GObject *object, case PROP_MODULE_PATH: g_value_set_string (value, core_config->module_path); break; + case PROP_INTERPRETER_PATH: + g_value_set_string (value, core_config->interpreter_path); + break; case PROP_ENVIRON_PATH: g_value_set_string (value, core_config->environ_path); break; diff --git a/app/config/gimpcoreconfig.h b/app/config/gimpcoreconfig.h index f16344a620..2bea1f7a94 100644 --- a/app/config/gimpcoreconfig.h +++ b/app/config/gimpcoreconfig.h @@ -43,6 +43,7 @@ struct _GimpCoreConfig GimpInterpolationType interpolation_type; gchar *plug_in_path; gchar *module_path; + gchar *interpreter_path; gchar *environ_path; gchar *brush_path; gchar *brush_path_writable; diff --git a/app/config/gimprc-blurbs.h b/app/config/gimprc-blurbs.h index 8fb7e7205c..e8a2dfed69 100644 --- a/app/config/gimprc-blurbs.h +++ b/app/config/gimprc-blurbs.h @@ -173,6 +173,9 @@ N_("Install a private colormap; might be useful on 8-bit (256 colors) displays." N_("Sets the level of interpolation used for scaling and other " \ "transformations.") +#define INTERPRETER_PATH_BLURB \ +"Sets the interpreter search path." + #define LAST_OPENED_SIZE_BLURB \ N_("How many recently opened image filenames to keep on the File menu.") diff --git a/app/core/Makefile.am b/app/core/Makefile.am index e87d33cc7c..65855218a4 100644 --- a/app/core/Makefile.am +++ b/app/core/Makefile.am @@ -158,6 +158,8 @@ libappcore_a_sources = \ gimpimagefile.h \ gimpimagemap.c \ gimpimagemap.h \ + gimpinterpreterdb.c \ + gimpinterpreterdb.h \ gimpitem.c \ gimpitem.h \ gimpitem-linked.c \ diff --git a/app/core/core-types.h b/app/core/core-types.h index 3c66df1444..d253c104ce 100644 --- a/app/core/core-types.h +++ b/app/core/core-types.h @@ -113,6 +113,7 @@ typedef struct _GimpEnvironTable GimpEnvironTable; /* typedef struct _GimpGrid GimpGrid; in config-types.h */ typedef struct _GimpImagefile GimpImagefile; typedef struct _GimpImageMap GimpImageMap; +typedef struct _GimpInterpreterDB GimpInterpreterDB; typedef struct _GimpParasiteList GimpParasiteList; typedef struct _GimpPdbProgress GimpPdbProgress; typedef struct _GimpProjection GimpProjection; diff --git a/app/core/gimp.c b/app/core/gimp.c index e87a55d98e..041aae4ef6 100644 --- a/app/core/gimp.c +++ b/app/core/gimp.c @@ -59,6 +59,7 @@ #include "gimpgradient-load.h" #include "gimpimage.h" #include "gimpimagefile.h" +#include "gimpinterpreterdb.h" #include "gimplist.h" #include "gimpmarshal.h" #include "gimppalette.h" @@ -230,6 +231,7 @@ gimp_init (Gimp *gimp) gimp_modules_init (gimp); + gimp->interpreter_db = gimp_interpreter_db_new (); gimp->environ_table = gimp_environ_table_new (); gimp->plug_in_debug = NULL; @@ -432,6 +434,12 @@ gimp_finalize (GObject *object) gimp->environ_table = NULL; } + if (gimp->interpreter_db) + { + g_object_unref (gimp->interpreter_db); + gimp->interpreter_db = NULL; + } + if (gimp->module_db) gimp_modules_exit (gimp); @@ -635,6 +643,12 @@ gimp_real_initialize (Gimp *gimp, (* status_callback) (_("Procedural Database"), NULL, -1); procedural_db_init_procs (gimp, status_callback); + (* status_callback) (_("Plug-In Interpreters"), "", -1); + + path = gimp_config_path_expand (gimp->config->interpreter_path, TRUE, NULL); + gimp_interpreter_db_load (gimp->interpreter_db, path); + g_free (path); + (* status_callback) (_("Plug-In Environment"), "", -1); path = gimp_config_path_expand (gimp->config->environ_path, TRUE, NULL); diff --git a/app/core/gimp.h b/app/core/gimp.h index db152b7af3..0459172266 100644 --- a/app/core/gimp.h +++ b/app/core/gimp.h @@ -81,6 +81,7 @@ struct _Gimp PlugInProcDef *last_plug_in; PlugInShm *plug_in_shm; + GimpInterpreterDB *interpreter_db; GimpEnvironTable *environ_table; GimpPlugInDebug *plug_in_debug; diff --git a/app/core/gimpinterpreterdb.c b/app/core/gimpinterpreterdb.c new file mode 100644 index 0000000000..ae27d1fc48 --- /dev/null +++ b/app/core/gimpinterpreterdb.c @@ -0,0 +1,767 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpinterpreterdb.c + * (C) 2005 Manish Singh + * + * 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. + */ + +/* + * The binfmt_misc bits are derived from linux/fs/binfmt_misc.c + * Copyright (C) 1997 Richard Günther + */ + +/* + * The sh-bang code is derived from linux/fs/binfmt_script.c + * Copyright (C) 1996 Martin von Löwis + * original #!-checking implemented by tytso. + */ + +#include "config.h" + +#include +#include +#include +#include + +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include + +#include +#include + +#ifdef G_OS_WIN32 +#include +#endif + +#ifndef _O_BINARY +#define _O_BINARY 0 +#endif + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gimpinterpreterdb.h" + +#include "gimp-intl.h" + + +#define BUFSIZE 4096 + + +typedef struct _GimpInterpreterMagic GimpInterpreterMagic; + +struct _GimpInterpreterMagic +{ + gulong offset; + gchar *magic; + gchar *mask; + guint size; + gchar *program; +}; + + +static void gimp_interpreter_db_class_init (GimpInterpreterDBClass *class); +static void gimp_interpreter_db_init (GimpInterpreterDB *db); + +static void gimp_interpreter_db_finalize (GObject *object); + +static void gimp_interpreter_db_load_interp_file (const GimpDatafileData *file_data, + gpointer user_data); + +static void gimp_interpreter_db_add_program (GimpInterpreterDB *db, + const GimpDatafileData *file_data, + gchar *buffer); +static void gimp_interpreter_db_add_binfmt_misc (GimpInterpreterDB *db, + const GimpDatafileData *file_data, + gchar *buffer); + +static gboolean gimp_interpreter_db_add_extension (GimpInterpreterDB *db, + gchar **tokens); +static gboolean gimp_interpreter_db_add_magic (GimpInterpreterDB *db, + gchar **tokens); + +static void gimp_interpreter_db_clear_magics (GimpInterpreterDB *db); + +static void gimp_interpreter_db_resolve_programs (GimpInterpreterDB *db); + + +static GObjectClass *parent_class = NULL; + + +GType +gimp_interpreter_db_get_type (void) +{ + static GType interpreter_db_type = 0; + + if (! interpreter_db_type) + { + static const GTypeInfo interpreter_db_info = + { + sizeof (GimpInterpreterDBClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) gimp_interpreter_db_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GimpInterpreterDB), + 0, /* n_preallocs */ + (GInstanceInitFunc) gimp_interpreter_db_init, + }; + + interpreter_db_type = g_type_register_static (G_TYPE_OBJECT, + "GimpInterpreterDB", + &interpreter_db_info, 0); + } + + return interpreter_db_type; +} + +static void +gimp_interpreter_db_class_init (GimpInterpreterDBClass *class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (class); + + parent_class = g_type_class_peek_parent (class); + + object_class->finalize = gimp_interpreter_db_finalize; +} + +static void +gimp_interpreter_db_init (GimpInterpreterDB *db) +{ + db->programs = NULL; + + db->magics = NULL; + db->magic_names = NULL; + + db->extensions = NULL; + db->extension_names = NULL; +} + +static void +gimp_interpreter_db_finalize (GObject *object) +{ + GimpInterpreterDB *db; + + db = GIMP_INTERPRETER_DB (object); + + gimp_interpreter_db_clear (db); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +GimpInterpreterDB * +gimp_interpreter_db_new (void) +{ + return g_object_new (GIMP_TYPE_INTERPRETER_DB, NULL); +} + +void +gimp_interpreter_db_load (GimpInterpreterDB *db, + const gchar *interp_path) +{ + g_return_if_fail (GIMP_IS_INTERPRETER_DB (db)); + + gimp_interpreter_db_clear (db); + + db->programs = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, g_free); + + db->extensions = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, g_free); + + db->magic_names = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + + db->extension_names = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + + gimp_datafiles_read_directories (interp_path, + G_FILE_TEST_EXISTS, + gimp_interpreter_db_load_interp_file, + db); + + gimp_interpreter_db_resolve_programs (db); +} + +void +gimp_interpreter_db_clear (GimpInterpreterDB *db) +{ + g_return_if_fail (GIMP_IS_INTERPRETER_DB (db)); + + if (db->magic_names) + { + g_hash_table_destroy (db->magic_names); + db->magic_names = NULL; + } + + if (db->extension_names) + { + g_hash_table_destroy (db->extension_names); + db->extension_names = NULL; + } + + if (db->programs) + { + g_hash_table_destroy (db->programs); + db->programs = NULL; + } + + if (db->extensions) + { + g_hash_table_destroy (db->extensions); + db->extensions = NULL; + } + + gimp_interpreter_db_clear_magics (db); +} + +static void +gimp_interpreter_db_load_interp_file (const GimpDatafileData *file_data, + gpointer user_data) +{ + GimpInterpreterDB *db; + FILE *interp_file; + gchar buffer[4096]; + gsize len; + + db = GIMP_INTERPRETER_DB (user_data); + + interp_file = g_fopen (file_data->filename, "r"); + if (! interp_file) + return; + + while (fgets (buffer, sizeof (buffer), interp_file)) + { + /* Skip comments */ + if (buffer[0] == '#') + continue; + + len = strlen (buffer) - 1; + + /* Skip too long lines */ + if (buffer[len] != '\n') + continue; + + buffer[len] = '\0'; + + if (g_ascii_isalnum (buffer[0]) || (buffer[0] == '/')) + gimp_interpreter_db_add_program (db, file_data, buffer); + else if (! g_ascii_isspace (buffer[0]) && (buffer[0] != '\0')) + gimp_interpreter_db_add_binfmt_misc (db, file_data, buffer); + } + + fclose (interp_file); +} + +static void +gimp_interpreter_db_add_program (GimpInterpreterDB *db, + const GimpDatafileData *file_data, + gchar *buffer) +{ + gchar *name, *program, *p; + + p = strchr (buffer, '='); + if (! p) + return; + + *p = '\0'; + + name = buffer; + program = p + 1; + + if (! g_file_test (program, G_FILE_TEST_IS_EXECUTABLE)) + { + g_message (_("Bad interpreter referenced in interpreter file %s: %s"), + gimp_filename_to_utf8 (file_data->filename), + gimp_filename_to_utf8 (program)); + return; + } + + if (! g_hash_table_lookup (db->programs, name)) + g_hash_table_insert (db->programs, g_strdup (name), g_strdup (program)); +} + +static void +gimp_interpreter_db_add_binfmt_misc (GimpInterpreterDB *db, + const GimpDatafileData *file_data, + gchar *buffer) +{ + gchar **tokens = NULL; + gchar *name, *type, *program; + gsize count; + gchar del[2]; + + count = strlen (buffer); + + if ((count < 10) || (count > 255)) + goto bail; + + del[0] = *buffer; + del[1] = '\0'; + + memset (buffer + count, del[0], 8); + + tokens = g_strsplit (buffer + 1, del, -1); + + name = tokens[0]; + type = tokens[1]; + program = tokens[5]; + + if (name[0] == '\0' || program == '\0' || type[0] == '\0' || type[1] != '\0') + goto bail; + + switch (type[0]) + { + case 'E': + if (! gimp_interpreter_db_add_extension (db, tokens)) + goto bail; + break; + + case 'M': + if (! gimp_interpreter_db_add_magic (db, tokens)) + goto bail; + break; + + default: + goto bail; + } + + goto out; + +bail: + g_message (_("Bad binary format string in interpreter file %s"), + gimp_filename_to_utf8 (file_data->filename)); + +out: + g_strfreev (tokens); +} + +static gboolean +gimp_interpreter_db_add_extension (GimpInterpreterDB *db, + gchar **tokens) +{ + gchar *name, *extension, *program; + + name = tokens[0]; + extension = tokens[3]; + program = tokens[5]; + + if (! g_hash_table_lookup (db->extension_names, name)) + { + if (extension[0] == '\0' || extension[0] == '/') + return FALSE; + + program = g_strdup (program); + + g_hash_table_insert (db->extensions, g_strdup (extension), program); + + g_hash_table_insert (db->extension_names, g_strdup (name), program); + } + + return TRUE; +} + +static gboolean +scanarg (gchar *s) +{ + gchar c; + + while ((c = *s++) != '\0') + { + if (c == '\\' && *s == 'x') + { + s++; + + if (! g_ascii_isxdigit (*s++)) + return FALSE; + + if (! g_ascii_isxdigit (*s++)) + return FALSE; + } + } + + return TRUE; +} + +static guint +unquote (gchar *from) +{ + gchar c, *s = from, *p = from; + + while ((c = *s++) != '\0') + { + if (c == '\\' && *s == 'x') + { + s++; + *p = g_ascii_xdigit_value (*s++) << 4; + *p++ |= g_ascii_xdigit_value (*s++); + continue; + } + + *p++ = c; + } + + return p - from; +} + +static gboolean +gimp_interpreter_db_add_magic (GimpInterpreterDB *db, + gchar **tokens) +{ + GimpInterpreterMagic *interp_magic; + gchar *name, *num, *magic, *mask, *program; + gulong offset; + guint size; + + name = tokens[0]; + num = tokens[2]; + magic = tokens[3]; + mask = tokens[4]; + program = tokens[5]; + + if (! g_hash_table_lookup (db->magic_names, name)) + { + if (num[0] != '\0') + { + offset = strtoul (num, &num, 10); + + if (num[0] != '\0') + return FALSE; + + if (offset > (BUFSIZE / 4)) + return FALSE; + } + else + offset = 0; + + if (! scanarg (magic)) + return FALSE; + + if (! scanarg (mask)) + return FALSE; + + size = unquote (magic); + + if ((size + offset) > (BUFSIZE / 2)) + return FALSE; + + if (mask[0] == '\0') + mask = NULL; + else if (unquote (mask) != size) + return FALSE; + + interp_magic = g_new (GimpInterpreterMagic, 1); + + interp_magic->offset = offset; + interp_magic->magic = g_memdup (magic, size); + interp_magic->mask = g_memdup (mask, size); + interp_magic->size = size; + interp_magic->program = g_strdup (program); + + db->magics = g_slist_append (db->magics, interp_magic); + + g_hash_table_insert (db->magic_names, g_strdup (name), interp_magic); + } + + return TRUE; +} + +static void +gimp_interpreter_db_clear_magics (GimpInterpreterDB *db) +{ + GimpInterpreterMagic *magic; + GSList *list, *last; + + list = db->magics; + db->magics = NULL; + + while (list) + { + magic = list->data; + + g_free (magic->magic); + g_free (magic->mask); + g_free (magic->program); + g_free (magic); + + last = list; + list = list->next; + + g_slist_free_1 (last); + } +} + +#ifdef INTERP_DEBUG +static void +print_kv (gpointer key, + gpointer value, + gpointer user_data) +{ + g_print ("%s: %s\n", (gchar *) key, (gchar *) value); +} + +static gchar * +quote (gchar *s, + guint size) +{ + GString *d; + guint i; + + if (s == NULL) + return "(null)"; + + d = g_string_sized_new (size * 4); + + for (i = 0; i < size; i++) + g_string_append_printf (d, "\\x%02x", ((guint) s[i]) & 0xff); + + return g_string_free (d, FALSE); +} +#endif + +static gboolean +resolve_program (gpointer key, + gpointer value, + gpointer user_data) +{ + GimpInterpreterDB *db = user_data; + gchar *program; + + program = g_hash_table_lookup (db->programs, value); + + if (program != NULL) + { + g_free (value); + value = g_strdup (program); + } + + g_hash_table_insert (db->extensions, key, value); + + return TRUE; +} + +static void +gimp_interpreter_db_resolve_programs (GimpInterpreterDB *db) +{ + GSList *list; + gchar *program; + GimpInterpreterMagic *magic; + GHashTable *extensions; + + list = db->magics; + + while (list) + { + magic = list->data; + + program = g_hash_table_lookup (db->programs, magic->program); + + if (program != NULL) + { + g_free (magic->program); + magic->program = g_strdup (program); + } + + list = list->next; + } + + extensions = db->extensions; + db->extensions = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, g_free); + + g_hash_table_foreach_steal (extensions, resolve_program, db); + + g_hash_table_destroy (extensions); + +#ifdef INTERP_DEBUG + g_print ("Programs:\n"); + g_hash_table_foreach (db->programs, print_kv, NULL); + + g_print ("\nExtensions:\n"); + g_hash_table_foreach (db->extensions, print_kv, NULL); + + g_print ("\nMagics:\n"); + + list = db->magics; + + while (list) + { + GimpInterpreterMagic *magic; + + magic = list->data; + g_print ("program: %s, offset: %lu, magic: %s, mask: %s\n", + magic->program, magic->offset, + quote (magic->magic, magic->size), + quote (magic->mask, magic->size)); + + list = list->next; + } + + g_print ("\n"); +#endif +} + +static gchar * +resolve_extension (GimpInterpreterDB *db, + const gchar *program_path) +{ + gchar *filename, *p, *program; + + filename = g_path_get_basename (program_path); + + p = strrchr (filename, '.'); + if (! p) + return NULL; + + program = g_hash_table_lookup (db->extensions, p + 1); + + return g_strdup (program); +} + +static gchar * +resolve_sh_bang (GimpInterpreterDB *db, + const gchar *program_path, + gchar *buffer, + gssize len, + gchar **interp_arg) +{ + gchar *cp, *name, *program; + + cp = strchr (buffer, '\n'); + if (! cp) + cp = buffer + len - 1; + + *cp = '\0'; + + while (cp > buffer) + { + cp--; + if ((*cp == ' ') || (*cp == '\t')) + *cp = '\0'; + else + break; + } + + for (cp = buffer + 2; (*cp == ' ') || (*cp == '\t'); cp++); + + if (*cp == '\0') + return NULL; + + name = cp; + + for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++) + /* nothing */ ; + + while ((*cp == ' ') || (*cp == '\t')) + *cp++ = '\0'; + + if (*cp) + { + if (strcmp ("/usr/bin/env", name) == 0) + { + program = g_hash_table_lookup (db->programs, cp); + if (program) + return g_strdup (program); + } + + *interp_arg = g_strdup (cp); + } + + program = g_hash_table_lookup (db->programs, name); + if (! program) + program = name; + + return g_strdup (program); +} + +static gchar * +resolve_magic (GimpInterpreterDB *db, + const gchar *program_path, + gchar *buffer) +{ + GSList *list; + GimpInterpreterMagic *magic; + gchar *s; + guint i; + + list = db->magics; + + while (list) + { + magic = list->data; + + s = buffer + magic->offset; + + if (magic->mask) + { + for (i = 0; i < magic->size; i++) + if ((*s++ ^ magic->magic[i]) & magic->mask[i]) + break; + } + else + { + for (i = 0; i < magic->size; i++) + if ((*s++ ^ magic->magic[i])) + break; + } + + if (i == magic->size) + return g_strdup (magic->program); + + list = list->next; + } + + return resolve_extension (db, program_path); +} + +gchar * +gimp_interpreter_db_resolve (GimpInterpreterDB *db, + const gchar *program_path, + gchar **interp_arg) +{ + gint fd; + gssize len; + gchar buffer[BUFSIZE]; + + *interp_arg = NULL; + + fd = g_open (program_path, O_RDONLY | _O_BINARY, 0); + + if (fd == -1) + return resolve_extension (db, program_path); + + memset (buffer, 0, sizeof (buffer)); + len = read (fd, buffer, sizeof (buffer)); + close (fd); + + if (len <= 0) + return resolve_extension (db, program_path); + + if (len > 3 && buffer[0] == '#' && buffer[1] == '!') + return resolve_sh_bang (db, program_path, buffer, len, interp_arg); + + return resolve_magic (db, program_path, buffer); +} diff --git a/app/core/gimpinterpreterdb.h b/app/core/gimpinterpreterdb.h new file mode 100644 index 0000000000..a3c155fa6b --- /dev/null +++ b/app/core/gimpinterpreterdb.h @@ -0,0 +1,68 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpinterpreterdb.h + * (C) 2005 Manish Singh + * + * 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. + */ + +#ifndef __GIMP_INTERPRETER_DB_H__ +#define __GIMP_INTERPRETER_DB_H__ + + +#define GIMP_TYPE_INTERPRETER_DB (gimp_interpreter_db_get_type ()) +#define GIMP_INTERPRETER_DB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_INTERPRETER_DB, GimpInterpreterDB)) +#define GIMP_INTERPRETER_DB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_INTERPRETER_DB, GimpInterpreterDBClass)) +#define GIMP_IS_INTERPRETER_DB(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_INTERPRETER_DB)) +#define GIMP_IS_INTERPRETER_DB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_INTERPRETER_DB)) +#define GIMP_INTERPRETER_DB_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_INTERPRETER_DB, GimpInterpreterDBClass)) + + +typedef struct _GimpInterpreterDBClass GimpInterpreterDBClass; + +struct _GimpInterpreterDB +{ + GObject parent_instance; + + GHashTable *programs; + + GSList *magics; + GHashTable *magic_names; + + GHashTable *extensions; + GHashTable *extension_names; +}; + +struct _GimpInterpreterDBClass +{ + GObjectClass parent_class; +}; + + +GType gimp_interpreter_db_get_type (void) G_GNUC_CONST; +GimpInterpreterDB * gimp_interpreter_db_new (void); + +void gimp_interpreter_db_load (GimpInterpreterDB *db, + const gchar *interp_path); + +void gimp_interpreter_db_clear (GimpInterpreterDB *db); + +gchar * gimp_interpreter_db_resolve (GimpInterpreterDB *db, + const gchar *program_path, + gchar **interp_arg); + + +#endif /* __GIMP_INTERPRETER_DB_H__ */ diff --git a/app/dialogs/preferences-dialog.c b/app/dialogs/preferences-dialog.c index 3773054323..838b77b4e0 100644 --- a/app/dialogs/preferences-dialog.c +++ b/app/dialogs/preferences-dialog.c @@ -2396,6 +2396,10 @@ prefs_dialog_new (Gimp *gimp, GIMP_HELP_PREFS_FOLDERS_MODULES, N_("Select Module Folders"), "module-path", NULL }, + { N_("Interpreters"), N_("Interpreter Folders"), "folders-interp.png", + GIMP_HELP_PREFS_FOLDERS_INTERPRETERS, + N_("Select Interpreter Folders"), + "interpreter-path", NULL }, { N_("Environment"), N_("Environment Folders"), "folders-environ.png", GIMP_HELP_PREFS_FOLDERS_ENVIRONMENT, N_("Select Environment Folders"), diff --git a/app/dialogs/user-install-dialog.c b/app/dialogs/user-install-dialog.c index eeab61f80f..55db25aa5b 100644 --- a/app/dialogs/user-install-dialog.c +++ b/app/dialogs/user-install-dialog.c @@ -243,6 +243,16 @@ tree_items[] = "during initialization."), TREE_ITEM_MKDIR }, + { + TRUE, "interpreters", + N_("This folder is used to store configuration for user " + "created, temporary, or otherwise non-system-supported " + "plug-in interpreters. The GIMP checks this folder in " + "addition to the system-wide GIMP interpreters folder " + "when searching for plug-in interpreter configuration " + "files."), + TREE_ITEM_MKDIR + }, { TRUE, "environ", N_("This folder is used to store user created, temporary, " diff --git a/app/plug-in/gimpinterpreterdb.c b/app/plug-in/gimpinterpreterdb.c new file mode 100644 index 0000000000..ae27d1fc48 --- /dev/null +++ b/app/plug-in/gimpinterpreterdb.c @@ -0,0 +1,767 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpinterpreterdb.c + * (C) 2005 Manish Singh + * + * 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. + */ + +/* + * The binfmt_misc bits are derived from linux/fs/binfmt_misc.c + * Copyright (C) 1997 Richard Günther + */ + +/* + * The sh-bang code is derived from linux/fs/binfmt_script.c + * Copyright (C) 1996 Martin von Löwis + * original #!-checking implemented by tytso. + */ + +#include "config.h" + +#include +#include +#include +#include + +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include + +#include +#include + +#ifdef G_OS_WIN32 +#include +#endif + +#ifndef _O_BINARY +#define _O_BINARY 0 +#endif + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gimpinterpreterdb.h" + +#include "gimp-intl.h" + + +#define BUFSIZE 4096 + + +typedef struct _GimpInterpreterMagic GimpInterpreterMagic; + +struct _GimpInterpreterMagic +{ + gulong offset; + gchar *magic; + gchar *mask; + guint size; + gchar *program; +}; + + +static void gimp_interpreter_db_class_init (GimpInterpreterDBClass *class); +static void gimp_interpreter_db_init (GimpInterpreterDB *db); + +static void gimp_interpreter_db_finalize (GObject *object); + +static void gimp_interpreter_db_load_interp_file (const GimpDatafileData *file_data, + gpointer user_data); + +static void gimp_interpreter_db_add_program (GimpInterpreterDB *db, + const GimpDatafileData *file_data, + gchar *buffer); +static void gimp_interpreter_db_add_binfmt_misc (GimpInterpreterDB *db, + const GimpDatafileData *file_data, + gchar *buffer); + +static gboolean gimp_interpreter_db_add_extension (GimpInterpreterDB *db, + gchar **tokens); +static gboolean gimp_interpreter_db_add_magic (GimpInterpreterDB *db, + gchar **tokens); + +static void gimp_interpreter_db_clear_magics (GimpInterpreterDB *db); + +static void gimp_interpreter_db_resolve_programs (GimpInterpreterDB *db); + + +static GObjectClass *parent_class = NULL; + + +GType +gimp_interpreter_db_get_type (void) +{ + static GType interpreter_db_type = 0; + + if (! interpreter_db_type) + { + static const GTypeInfo interpreter_db_info = + { + sizeof (GimpInterpreterDBClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) gimp_interpreter_db_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GimpInterpreterDB), + 0, /* n_preallocs */ + (GInstanceInitFunc) gimp_interpreter_db_init, + }; + + interpreter_db_type = g_type_register_static (G_TYPE_OBJECT, + "GimpInterpreterDB", + &interpreter_db_info, 0); + } + + return interpreter_db_type; +} + +static void +gimp_interpreter_db_class_init (GimpInterpreterDBClass *class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (class); + + parent_class = g_type_class_peek_parent (class); + + object_class->finalize = gimp_interpreter_db_finalize; +} + +static void +gimp_interpreter_db_init (GimpInterpreterDB *db) +{ + db->programs = NULL; + + db->magics = NULL; + db->magic_names = NULL; + + db->extensions = NULL; + db->extension_names = NULL; +} + +static void +gimp_interpreter_db_finalize (GObject *object) +{ + GimpInterpreterDB *db; + + db = GIMP_INTERPRETER_DB (object); + + gimp_interpreter_db_clear (db); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +GimpInterpreterDB * +gimp_interpreter_db_new (void) +{ + return g_object_new (GIMP_TYPE_INTERPRETER_DB, NULL); +} + +void +gimp_interpreter_db_load (GimpInterpreterDB *db, + const gchar *interp_path) +{ + g_return_if_fail (GIMP_IS_INTERPRETER_DB (db)); + + gimp_interpreter_db_clear (db); + + db->programs = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, g_free); + + db->extensions = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, g_free); + + db->magic_names = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + + db->extension_names = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + + gimp_datafiles_read_directories (interp_path, + G_FILE_TEST_EXISTS, + gimp_interpreter_db_load_interp_file, + db); + + gimp_interpreter_db_resolve_programs (db); +} + +void +gimp_interpreter_db_clear (GimpInterpreterDB *db) +{ + g_return_if_fail (GIMP_IS_INTERPRETER_DB (db)); + + if (db->magic_names) + { + g_hash_table_destroy (db->magic_names); + db->magic_names = NULL; + } + + if (db->extension_names) + { + g_hash_table_destroy (db->extension_names); + db->extension_names = NULL; + } + + if (db->programs) + { + g_hash_table_destroy (db->programs); + db->programs = NULL; + } + + if (db->extensions) + { + g_hash_table_destroy (db->extensions); + db->extensions = NULL; + } + + gimp_interpreter_db_clear_magics (db); +} + +static void +gimp_interpreter_db_load_interp_file (const GimpDatafileData *file_data, + gpointer user_data) +{ + GimpInterpreterDB *db; + FILE *interp_file; + gchar buffer[4096]; + gsize len; + + db = GIMP_INTERPRETER_DB (user_data); + + interp_file = g_fopen (file_data->filename, "r"); + if (! interp_file) + return; + + while (fgets (buffer, sizeof (buffer), interp_file)) + { + /* Skip comments */ + if (buffer[0] == '#') + continue; + + len = strlen (buffer) - 1; + + /* Skip too long lines */ + if (buffer[len] != '\n') + continue; + + buffer[len] = '\0'; + + if (g_ascii_isalnum (buffer[0]) || (buffer[0] == '/')) + gimp_interpreter_db_add_program (db, file_data, buffer); + else if (! g_ascii_isspace (buffer[0]) && (buffer[0] != '\0')) + gimp_interpreter_db_add_binfmt_misc (db, file_data, buffer); + } + + fclose (interp_file); +} + +static void +gimp_interpreter_db_add_program (GimpInterpreterDB *db, + const GimpDatafileData *file_data, + gchar *buffer) +{ + gchar *name, *program, *p; + + p = strchr (buffer, '='); + if (! p) + return; + + *p = '\0'; + + name = buffer; + program = p + 1; + + if (! g_file_test (program, G_FILE_TEST_IS_EXECUTABLE)) + { + g_message (_("Bad interpreter referenced in interpreter file %s: %s"), + gimp_filename_to_utf8 (file_data->filename), + gimp_filename_to_utf8 (program)); + return; + } + + if (! g_hash_table_lookup (db->programs, name)) + g_hash_table_insert (db->programs, g_strdup (name), g_strdup (program)); +} + +static void +gimp_interpreter_db_add_binfmt_misc (GimpInterpreterDB *db, + const GimpDatafileData *file_data, + gchar *buffer) +{ + gchar **tokens = NULL; + gchar *name, *type, *program; + gsize count; + gchar del[2]; + + count = strlen (buffer); + + if ((count < 10) || (count > 255)) + goto bail; + + del[0] = *buffer; + del[1] = '\0'; + + memset (buffer + count, del[0], 8); + + tokens = g_strsplit (buffer + 1, del, -1); + + name = tokens[0]; + type = tokens[1]; + program = tokens[5]; + + if (name[0] == '\0' || program == '\0' || type[0] == '\0' || type[1] != '\0') + goto bail; + + switch (type[0]) + { + case 'E': + if (! gimp_interpreter_db_add_extension (db, tokens)) + goto bail; + break; + + case 'M': + if (! gimp_interpreter_db_add_magic (db, tokens)) + goto bail; + break; + + default: + goto bail; + } + + goto out; + +bail: + g_message (_("Bad binary format string in interpreter file %s"), + gimp_filename_to_utf8 (file_data->filename)); + +out: + g_strfreev (tokens); +} + +static gboolean +gimp_interpreter_db_add_extension (GimpInterpreterDB *db, + gchar **tokens) +{ + gchar *name, *extension, *program; + + name = tokens[0]; + extension = tokens[3]; + program = tokens[5]; + + if (! g_hash_table_lookup (db->extension_names, name)) + { + if (extension[0] == '\0' || extension[0] == '/') + return FALSE; + + program = g_strdup (program); + + g_hash_table_insert (db->extensions, g_strdup (extension), program); + + g_hash_table_insert (db->extension_names, g_strdup (name), program); + } + + return TRUE; +} + +static gboolean +scanarg (gchar *s) +{ + gchar c; + + while ((c = *s++) != '\0') + { + if (c == '\\' && *s == 'x') + { + s++; + + if (! g_ascii_isxdigit (*s++)) + return FALSE; + + if (! g_ascii_isxdigit (*s++)) + return FALSE; + } + } + + return TRUE; +} + +static guint +unquote (gchar *from) +{ + gchar c, *s = from, *p = from; + + while ((c = *s++) != '\0') + { + if (c == '\\' && *s == 'x') + { + s++; + *p = g_ascii_xdigit_value (*s++) << 4; + *p++ |= g_ascii_xdigit_value (*s++); + continue; + } + + *p++ = c; + } + + return p - from; +} + +static gboolean +gimp_interpreter_db_add_magic (GimpInterpreterDB *db, + gchar **tokens) +{ + GimpInterpreterMagic *interp_magic; + gchar *name, *num, *magic, *mask, *program; + gulong offset; + guint size; + + name = tokens[0]; + num = tokens[2]; + magic = tokens[3]; + mask = tokens[4]; + program = tokens[5]; + + if (! g_hash_table_lookup (db->magic_names, name)) + { + if (num[0] != '\0') + { + offset = strtoul (num, &num, 10); + + if (num[0] != '\0') + return FALSE; + + if (offset > (BUFSIZE / 4)) + return FALSE; + } + else + offset = 0; + + if (! scanarg (magic)) + return FALSE; + + if (! scanarg (mask)) + return FALSE; + + size = unquote (magic); + + if ((size + offset) > (BUFSIZE / 2)) + return FALSE; + + if (mask[0] == '\0') + mask = NULL; + else if (unquote (mask) != size) + return FALSE; + + interp_magic = g_new (GimpInterpreterMagic, 1); + + interp_magic->offset = offset; + interp_magic->magic = g_memdup (magic, size); + interp_magic->mask = g_memdup (mask, size); + interp_magic->size = size; + interp_magic->program = g_strdup (program); + + db->magics = g_slist_append (db->magics, interp_magic); + + g_hash_table_insert (db->magic_names, g_strdup (name), interp_magic); + } + + return TRUE; +} + +static void +gimp_interpreter_db_clear_magics (GimpInterpreterDB *db) +{ + GimpInterpreterMagic *magic; + GSList *list, *last; + + list = db->magics; + db->magics = NULL; + + while (list) + { + magic = list->data; + + g_free (magic->magic); + g_free (magic->mask); + g_free (magic->program); + g_free (magic); + + last = list; + list = list->next; + + g_slist_free_1 (last); + } +} + +#ifdef INTERP_DEBUG +static void +print_kv (gpointer key, + gpointer value, + gpointer user_data) +{ + g_print ("%s: %s\n", (gchar *) key, (gchar *) value); +} + +static gchar * +quote (gchar *s, + guint size) +{ + GString *d; + guint i; + + if (s == NULL) + return "(null)"; + + d = g_string_sized_new (size * 4); + + for (i = 0; i < size; i++) + g_string_append_printf (d, "\\x%02x", ((guint) s[i]) & 0xff); + + return g_string_free (d, FALSE); +} +#endif + +static gboolean +resolve_program (gpointer key, + gpointer value, + gpointer user_data) +{ + GimpInterpreterDB *db = user_data; + gchar *program; + + program = g_hash_table_lookup (db->programs, value); + + if (program != NULL) + { + g_free (value); + value = g_strdup (program); + } + + g_hash_table_insert (db->extensions, key, value); + + return TRUE; +} + +static void +gimp_interpreter_db_resolve_programs (GimpInterpreterDB *db) +{ + GSList *list; + gchar *program; + GimpInterpreterMagic *magic; + GHashTable *extensions; + + list = db->magics; + + while (list) + { + magic = list->data; + + program = g_hash_table_lookup (db->programs, magic->program); + + if (program != NULL) + { + g_free (magic->program); + magic->program = g_strdup (program); + } + + list = list->next; + } + + extensions = db->extensions; + db->extensions = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, g_free); + + g_hash_table_foreach_steal (extensions, resolve_program, db); + + g_hash_table_destroy (extensions); + +#ifdef INTERP_DEBUG + g_print ("Programs:\n"); + g_hash_table_foreach (db->programs, print_kv, NULL); + + g_print ("\nExtensions:\n"); + g_hash_table_foreach (db->extensions, print_kv, NULL); + + g_print ("\nMagics:\n"); + + list = db->magics; + + while (list) + { + GimpInterpreterMagic *magic; + + magic = list->data; + g_print ("program: %s, offset: %lu, magic: %s, mask: %s\n", + magic->program, magic->offset, + quote (magic->magic, magic->size), + quote (magic->mask, magic->size)); + + list = list->next; + } + + g_print ("\n"); +#endif +} + +static gchar * +resolve_extension (GimpInterpreterDB *db, + const gchar *program_path) +{ + gchar *filename, *p, *program; + + filename = g_path_get_basename (program_path); + + p = strrchr (filename, '.'); + if (! p) + return NULL; + + program = g_hash_table_lookup (db->extensions, p + 1); + + return g_strdup (program); +} + +static gchar * +resolve_sh_bang (GimpInterpreterDB *db, + const gchar *program_path, + gchar *buffer, + gssize len, + gchar **interp_arg) +{ + gchar *cp, *name, *program; + + cp = strchr (buffer, '\n'); + if (! cp) + cp = buffer + len - 1; + + *cp = '\0'; + + while (cp > buffer) + { + cp--; + if ((*cp == ' ') || (*cp == '\t')) + *cp = '\0'; + else + break; + } + + for (cp = buffer + 2; (*cp == ' ') || (*cp == '\t'); cp++); + + if (*cp == '\0') + return NULL; + + name = cp; + + for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++) + /* nothing */ ; + + while ((*cp == ' ') || (*cp == '\t')) + *cp++ = '\0'; + + if (*cp) + { + if (strcmp ("/usr/bin/env", name) == 0) + { + program = g_hash_table_lookup (db->programs, cp); + if (program) + return g_strdup (program); + } + + *interp_arg = g_strdup (cp); + } + + program = g_hash_table_lookup (db->programs, name); + if (! program) + program = name; + + return g_strdup (program); +} + +static gchar * +resolve_magic (GimpInterpreterDB *db, + const gchar *program_path, + gchar *buffer) +{ + GSList *list; + GimpInterpreterMagic *magic; + gchar *s; + guint i; + + list = db->magics; + + while (list) + { + magic = list->data; + + s = buffer + magic->offset; + + if (magic->mask) + { + for (i = 0; i < magic->size; i++) + if ((*s++ ^ magic->magic[i]) & magic->mask[i]) + break; + } + else + { + for (i = 0; i < magic->size; i++) + if ((*s++ ^ magic->magic[i])) + break; + } + + if (i == magic->size) + return g_strdup (magic->program); + + list = list->next; + } + + return resolve_extension (db, program_path); +} + +gchar * +gimp_interpreter_db_resolve (GimpInterpreterDB *db, + const gchar *program_path, + gchar **interp_arg) +{ + gint fd; + gssize len; + gchar buffer[BUFSIZE]; + + *interp_arg = NULL; + + fd = g_open (program_path, O_RDONLY | _O_BINARY, 0); + + if (fd == -1) + return resolve_extension (db, program_path); + + memset (buffer, 0, sizeof (buffer)); + len = read (fd, buffer, sizeof (buffer)); + close (fd); + + if (len <= 0) + return resolve_extension (db, program_path); + + if (len > 3 && buffer[0] == '#' && buffer[1] == '!') + return resolve_sh_bang (db, program_path, buffer, len, interp_arg); + + return resolve_magic (db, program_path, buffer); +} diff --git a/app/plug-in/gimpinterpreterdb.h b/app/plug-in/gimpinterpreterdb.h new file mode 100644 index 0000000000..a3c155fa6b --- /dev/null +++ b/app/plug-in/gimpinterpreterdb.h @@ -0,0 +1,68 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpinterpreterdb.h + * (C) 2005 Manish Singh + * + * 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. + */ + +#ifndef __GIMP_INTERPRETER_DB_H__ +#define __GIMP_INTERPRETER_DB_H__ + + +#define GIMP_TYPE_INTERPRETER_DB (gimp_interpreter_db_get_type ()) +#define GIMP_INTERPRETER_DB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_INTERPRETER_DB, GimpInterpreterDB)) +#define GIMP_INTERPRETER_DB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_INTERPRETER_DB, GimpInterpreterDBClass)) +#define GIMP_IS_INTERPRETER_DB(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_INTERPRETER_DB)) +#define GIMP_IS_INTERPRETER_DB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_INTERPRETER_DB)) +#define GIMP_INTERPRETER_DB_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_INTERPRETER_DB, GimpInterpreterDBClass)) + + +typedef struct _GimpInterpreterDBClass GimpInterpreterDBClass; + +struct _GimpInterpreterDB +{ + GObject parent_instance; + + GHashTable *programs; + + GSList *magics; + GHashTable *magic_names; + + GHashTable *extensions; + GHashTable *extension_names; +}; + +struct _GimpInterpreterDBClass +{ + GObjectClass parent_class; +}; + + +GType gimp_interpreter_db_get_type (void) G_GNUC_CONST; +GimpInterpreterDB * gimp_interpreter_db_new (void); + +void gimp_interpreter_db_load (GimpInterpreterDB *db, + const gchar *interp_path); + +void gimp_interpreter_db_clear (GimpInterpreterDB *db); + +gchar * gimp_interpreter_db_resolve (GimpInterpreterDB *db, + const gchar *program_path, + gchar **interp_arg); + + +#endif /* __GIMP_INTERPRETER_DB_H__ */ diff --git a/app/plug-in/gimpplugin.c b/app/plug-in/gimpplugin.c index 7715a84413..059ef1e830 100644 --- a/app/plug-in/gimpplugin.c +++ b/app/plug-in/gimpplugin.c @@ -73,6 +73,7 @@ #include "core/gimp.h" #include "core/gimpcontext.h" #include "core/gimpenvirontable.h" +#include "core/gimpinterpreterdb.h" #include "core/gimpprogress.h" #include "plug-in.h" @@ -337,7 +338,9 @@ plug_in_open (PlugIn *plug_in) gint my_read[2]; gint my_write[2]; gchar **envp; - gchar *args[7], **argv, **debug_argv; + gchar *args[9], **argv, **debug_argv; + gint argc; + gchar *interp, *interp_arg; gchar *read_fd, *write_fd; gchar *mode, *stm; GError *error = NULL; @@ -417,13 +420,23 @@ plug_in_open (PlugIn *plug_in) stm = g_strdup_printf ("%d", plug_in->gimp->stack_trace_mode); - args[0] = plug_in->prog; - args[1] = "-gimp"; - args[2] = read_fd; - args[3] = write_fd; - args[4] = mode; - args[5] = stm; - args[6] = NULL; + interp = gimp_interpreter_db_resolve (plug_in->gimp->interpreter_db, + plug_in->prog, &interp_arg); + + argc = 0; + + if (interp) + args[argc++] = interp; + if (interp_arg) + args[argc++] = interp_arg; + + args[argc++] = plug_in->prog; + args[argc++] = "-gimp"; + args[argc++] = read_fd; + args[argc++] = write_fd; + args[argc++] = mode; + args[argc++] = stm; + args[argc++] = NULL; argv = args; envp = gimp_environ_table_get_envp (plug_in->gimp->environ_table); @@ -497,6 +510,9 @@ cleanup: g_free (write_fd); g_free (stm); + g_free (interp); + g_free (interp_arg); + return plug_in->open; } diff --git a/app/plug-in/plug-in.c b/app/plug-in/plug-in.c index 7715a84413..059ef1e830 100644 --- a/app/plug-in/plug-in.c +++ b/app/plug-in/plug-in.c @@ -73,6 +73,7 @@ #include "core/gimp.h" #include "core/gimpcontext.h" #include "core/gimpenvirontable.h" +#include "core/gimpinterpreterdb.h" #include "core/gimpprogress.h" #include "plug-in.h" @@ -337,7 +338,9 @@ plug_in_open (PlugIn *plug_in) gint my_read[2]; gint my_write[2]; gchar **envp; - gchar *args[7], **argv, **debug_argv; + gchar *args[9], **argv, **debug_argv; + gint argc; + gchar *interp, *interp_arg; gchar *read_fd, *write_fd; gchar *mode, *stm; GError *error = NULL; @@ -417,13 +420,23 @@ plug_in_open (PlugIn *plug_in) stm = g_strdup_printf ("%d", plug_in->gimp->stack_trace_mode); - args[0] = plug_in->prog; - args[1] = "-gimp"; - args[2] = read_fd; - args[3] = write_fd; - args[4] = mode; - args[5] = stm; - args[6] = NULL; + interp = gimp_interpreter_db_resolve (plug_in->gimp->interpreter_db, + plug_in->prog, &interp_arg); + + argc = 0; + + if (interp) + args[argc++] = interp; + if (interp_arg) + args[argc++] = interp_arg; + + args[argc++] = plug_in->prog; + args[argc++] = "-gimp"; + args[argc++] = read_fd; + args[argc++] = write_fd; + args[argc++] = mode; + args[argc++] = stm; + args[argc++] = NULL; argv = args; envp = gimp_environ_table_get_envp (plug_in->gimp->environ_table); @@ -497,6 +510,9 @@ cleanup: g_free (write_fd); g_free (stm); + g_free (interp); + g_free (interp_arg); + return plug_in->open; } diff --git a/app/widgets/gimphelp-ids.h b/app/widgets/gimphelp-ids.h index b7564b6f1c..1e1cbd61e1 100644 --- a/app/widgets/gimphelp-ids.h +++ b/app/widgets/gimphelp-ids.h @@ -399,6 +399,7 @@ #define GIMP_HELP_PREFS_FOLDERS_PLUG_INS "gimp-prefs-folders-plug-ins" #define GIMP_HELP_PREFS_FOLDERS_SCRIPTS "gimp-prefs-folders-scripts" #define GIMP_HELP_PREFS_FOLDERS_MODULES "gimp-prefs-folders-modules" +#define GIMP_HELP_PREFS_FOLDERS_INTERPRETERS "gimp-prefs-folders-interpreters" #define GIMP_HELP_PREFS_FOLDERS_ENVIRONMENT "gimp-prefs-folders-environment" #define GIMP_HELP_PREFS_FOLDERS_THEMES "gimp-prefs-folders-themes" diff --git a/configure.in b/configure.in index 14d8a5ed94..1ff605c9a3 100644 --- a/configure.in +++ b/configure.in @@ -1647,6 +1647,7 @@ themes/Default/images/Makefile themes/Default/images/preferences/Makefile themes/Small/Makefile data/Makefile +data/interpreters/Makefile data/environ/Makefile data/misc/Makefile data/misc/gimp.desktop.in diff --git a/data/Makefile.am b/data/Makefile.am index fc066380c9..7d4308a64c 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -1,6 +1,6 @@ ## Makefile.am for gimp/data -SUBDIRS = environ misc images brushes gradients palettes patterns +SUBDIRS = interpreters environ misc images brushes gradients palettes patterns EXTRA_DIST = \ AUTHORS \ diff --git a/data/interpreters/.cvsignore b/data/interpreters/.cvsignore new file mode 100644 index 0000000000..3dda72986f --- /dev/null +++ b/data/interpreters/.cvsignore @@ -0,0 +1,2 @@ +Makefile.in +Makefile diff --git a/data/interpreters/Makefile.am b/data/interpreters/Makefile.am new file mode 100644 index 0000000000..3dda7c7a45 --- /dev/null +++ b/data/interpreters/Makefile.am @@ -0,0 +1,7 @@ +## Makefile.am for gimp/data/interpreters + +interpretersdir = $(gimpplugindir)/interpreters + +interpreters_DATA = default.interp + +EXTRA_DIST = $(interpreters_DATA) diff --git a/data/interpreters/default.interp b/data/interpreters/default.interp new file mode 100644 index 0000000000..9e791bec03 --- /dev/null +++ b/data/interpreters/default.interp @@ -0,0 +1,15 @@ +# Example entries in files like these + +# This registers an interpreter name with a binary +# It is matched against #! syntax and the binfmt_misc syntax +#scm=/path/to/scm + +# The following is the same syntax as Linux binfmt_misc +# See Documentation/binfmt_misc.txt in the Linux kernel sources for +# detailed information. (Right now, "flags" are noops) + +# Register an extension +#:Scheme-ext:E::scm::/path/to/scm: + +# Register a magic +#:Scheme-magic:M::\x3b\x20GIMP Scheme::scm: diff --git a/docs/gimprc.5.in b/docs/gimprc.5.in index 6ba4d37e98..4db0f221b6 100644 --- a/docs/gimprc.5.in +++ b/docs/gimprc.5.in @@ -61,14 +61,14 @@ GIMP opts for speed over memory. However, if memory is a big issue, try to enable this setting. Possible values are yes and no. .TP -(num-processors 1) +(num-processors 2) On multiprocessor machines, if GIMP has been compiled with --enable-mp this sets how many processors GIMP should use simultaneously. This is an integer value. .TP -(tile-cache-size 128M) +(tile-cache-size 256M) The tile cache is used to make sure the GIMP doesn't thrash tiles between memory and disk. Setting this value higher will cause the GIMP to use less @@ -83,7 +83,7 @@ kilobytes. (interpolation-type linear) Sets the level of interpolation used for scaling and other transformations. -Possible values are none, linear and cubic. +Possible values are none, linear, cubic and lanczos. .TP (plug-in-path "${gimp_dir}/plug-ins:${gimp_plug_in_dir}/plug-ins") @@ -97,6 +97,12 @@ search. Sets the module search path. This is a colon-separated list of folders to search. +.TP +(interpreter-path "${gimp_dir}/interpreters:${gimp_plug_in_dir}/interpreters") + +Sets the interpreter search path. This is a colon-separated list of folders +to search. + .TP (environ-path "${gimp_dir}/environ:${gimp_plug_in_dir}/environ") @@ -219,8 +225,8 @@ are yes and no. .TP (default-image - (width 420) - (height 300) + (width 377) + (height 233) (unit pixels) (xresolution 72.000000) (yresolution 72.000000) @@ -236,8 +242,8 @@ Sets the default image in the "File/New" dialog. This is a parameter list. (style intersections) (fgcolor (color-rgba 0.000000 0.000000 0.000000 1.000000)) (bgcolor (color-rgba 1.000000 1.000000 1.000000 1.000000)) - (xspacing 10.000000) - (yspacing 10.000000) + (xspacing 32.000000) + (yspacing 32.000000) (spacing-unit inches) (xoffset 0.000000) (yoffset 0.000000) @@ -315,6 +321,14 @@ Possible values are yes and no. Generally only a concern for 8-bit displays, this sets the minimum number of system colors allocated for the GIMP. This is an integer value. +.TP +(color-management + (mode display) + (display-rendering-intent perceptual) + (simulation-rendering-intent perceptual)) + +Defines the color management behavior. This is a parameter list. + .TP (transparency-size medium-checks) diff --git a/etc/gimprc b/etc/gimprc index d8965f6ac1..b3d10e8e1e 100644 --- a/etc/gimprc +++ b/etc/gimprc @@ -39,7 +39,7 @@ # sets how many processors GIMP should use simultaneously. This is an # integer value. # -# (num-processors 1) +# (num-processors 2) # The tile cache is used to make sure the GIMP doesn't thrash tiles between # memory and disk. Setting this value higher will cause the GIMP to use less @@ -50,10 +50,10 @@ # or gigabytes. If no suffix is specified the size defaults to being # specified in kilobytes. # -# (tile-cache-size 128M) +# (tile-cache-size 256M) # Sets the level of interpolation used for scaling and other transformations. -# Possible values are none, linear and cubic. +# Possible values are none, linear, cubic and lanczos. # # (interpolation-type linear) @@ -67,6 +67,11 @@ # # (module-path "${gimp_dir}/modules:${gimp_plug_in_dir}/modules") +# Sets the interpreter search path. This is a colon-separated list of +# folders to search. +# +# (interpreter-path "${gimp_dir}/interpreters:${gimp_plug_in_dir}/interpreters") + # Sets the environ search path. This is a colon-separated list of folders to # search. # @@ -170,8 +175,8 @@ # Sets the default image in the "File/New" dialog. This is a parameter list. # # (default-image -# (width 420) -# (height 300) +# (width 377) +# (height 233) # (unit pixels) # (xresolution 72.000000) # (yresolution 72.000000) @@ -186,8 +191,8 @@ # (style intersections) # (fgcolor (color-rgba 0.000000 0.000000 0.000000 1.000000)) # (bgcolor (color-rgba 1.000000 1.000000 1.000000 1.000000)) -# (xspacing 10.000000) -# (yspacing 10.000000) +# (xspacing 32.000000) +# (yspacing 32.000000) # (spacing-unit inches) # (xoffset 0.000000) # (yoffset 0.000000) @@ -254,6 +259,13 @@ # # (min-colors 144) +# Defines the color management behavior. This is a parameter list. +# +# (color-management +# (mode display) +# (display-rendering-intent perceptual) +# (simulation-rendering-intent perceptual)) + # Sets the size of the checkerboard used to display transparency. Possible # values are small-checks, medium-checks and large-checks. # diff --git a/themes/Default/images/preferences/Makefile.am b/themes/Default/images/preferences/Makefile.am index 01164f9154..a07ff316b9 100644 --- a/themes/Default/images/preferences/Makefile.am +++ b/themes/Default/images/preferences/Makefile.am @@ -11,10 +11,11 @@ PREFS_IMAGES = \ environment.png \ folders.png \ folders-brushes.png \ + folders-environ.png \ folders-fonts.png \ folders-gradients.png \ + folders-interp.png \ folders-modules.png \ - folders-environ.png \ folders-palettes.png \ folders-patterns.png \ folders-plug-ins.png \ diff --git a/themes/Default/images/preferences/folders-interp.png b/themes/Default/images/preferences/folders-interp.png new file mode 100644 index 0000000000000000000000000000000000000000..c36e516bf393432adc765fc368b8a88fedeb0570 GIT binary patch literal 3709 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