diff --git a/app/plug-in/Makefile.am b/app/plug-in/Makefile.am
index 950783f420..4e036d68bd 100644
--- a/app/plug-in/Makefile.am
+++ b/app/plug-in/Makefile.am
@@ -49,8 +49,6 @@ libappplug_in_a_SOURCES = \
gimppluginmanager-data.h \
gimppluginmanager-file.c \
gimppluginmanager-file.h \
- gimppluginmanager-file-procedure.c \
- gimppluginmanager-file-procedure.h \
gimppluginmanager-help-domain.c \
gimppluginmanager-help-domain.h \
gimppluginmanager-locale-domain.c \
diff --git a/app/plug-in/gimppluginmanager-file-procedure.c b/app/plug-in/gimppluginmanager-file-procedure.c
deleted file mode 100644
index ec11ef607e..0000000000
--- a/app/plug-in/gimppluginmanager-file-procedure.c
+++ /dev/null
@@ -1,716 +0,0 @@
-/* GIMP - The GNU Image Manipulation Program
- * Copyright (C) 1995, 1996, 1997 Spencer Kimball and Peter Mattis
- * Copyright (C) 1997 Josh MacDonald
- *
- * gimppluginmanager-file-procedure.c
- *
- * 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 3 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, see .
- */
-
-#include "config.h"
-
-#include
-#include
-
-#include
-#include
-
-#include "libgimpbase/gimpbase.h"
-
-#include "plug-in-types.h"
-
-#include "core/gimp-utils.h"
-
-#include "gimppluginmanager-file-procedure.h"
-#include "gimppluginprocedure.h"
-
-#include "gimp-log.h"
-
-#include "gimp-intl.h"
-
-
-typedef enum
-{
- /* positive values indicate the length of a matching magic */
-
- FILE_MATCH_NONE = 0,
- FILE_MATCH_SIZE = -1
-} FileMatchType;
-
-
-/* local function prototypes */
-
-static GimpPlugInProcedure * file_proc_find_by_prefix (GSList *procs,
- GFile *file,
- gboolean skip_magic);
-static GimpPlugInProcedure * file_proc_find_by_extension (GSList *procs,
- GFile *file,
- gboolean skip_magic);
-static GimpPlugInProcedure * file_proc_find_by_name (GSList *procs,
- GFile *file,
- gboolean skip_magic);
-
-static void file_convert_string (const gchar *instr,
- gchar *outmem,
- gint maxmem,
- gint *nmem);
-static FileMatchType file_check_single_magic (const gchar *offset,
- const gchar *type,
- const gchar *value,
- const guchar *file_head,
- gint headsize,
- GFile *file,
- GInputStream *input);
-static FileMatchType file_check_magic_list (GSList *magics_list,
- const guchar *head,
- gint headsize,
- GFile *file,
- GInputStream *input);
-
-
-/* public functions */
-
-GimpPlugInProcedure *
-file_procedure_find (GSList *procs,
- GFile *file,
- GError **error)
-{
- GimpPlugInProcedure *file_proc;
- GimpPlugInProcedure *size_matched_proc = NULL;
- gint size_match_count = 0;
-
- g_return_val_if_fail (procs != NULL, NULL);
- g_return_val_if_fail (G_IS_FILE (file), NULL);
- g_return_val_if_fail (error == NULL || *error == NULL, NULL);
-
- /* First, check magicless prefixes/suffixes */
- file_proc = file_proc_find_by_name (procs, file, TRUE);
-
- if (file_proc)
- return file_proc;
-
- /* Then look for magics, but not on remote files */
- if (g_file_is_native (file))
- {
- GSList *list;
- GInputStream *input = NULL;
- gboolean opened = FALSE;
- gsize head_size = 0;
- guchar head[256];
- FileMatchType best_match_val = FILE_MATCH_NONE;
- GimpPlugInProcedure *best_file_proc = NULL;
-
- for (list = procs; list; list = g_slist_next (list))
- {
- file_proc = list->data;
-
- if (file_proc->magics_list)
- {
- if (G_UNLIKELY (! opened))
- {
- input = G_INPUT_STREAM (g_file_read (file, NULL, error));
-
- if (input)
- {
- g_input_stream_read_all (input,
- head, sizeof (head),
- &head_size, NULL, error);
-
- if (head_size < 4)
- {
- g_object_unref (input);
- input = NULL;
- }
- else
- {
- GDataInputStream *data_input;
-
- data_input = g_data_input_stream_new (input);
- g_object_unref (input);
- input = G_INPUT_STREAM (data_input);
- }
- }
-
- opened = TRUE;
- }
-
- if (head_size >= 4)
- {
- FileMatchType match_val;
-
- match_val = file_check_magic_list (file_proc->magics_list,
- head, head_size,
- file, input);
-
- if (match_val == FILE_MATCH_SIZE)
- {
- /* Use it only if no other magic matches */
- size_match_count++;
- size_matched_proc = file_proc;
- }
- else if (match_val != FILE_MATCH_NONE)
- {
- GIMP_LOG (MAGIC_MATCH,
- "magic match %d on %s\n",
- match_val,
- gimp_object_get_name (file_proc));
-
- if (match_val > best_match_val)
- {
- best_match_val = match_val;
- best_file_proc = file_proc;
- }
- }
- }
- }
- }
-
- if (input)
- g_object_unref (input);
-
- if (best_file_proc)
- {
- GIMP_LOG (MAGIC_MATCH,
- "best magic match on %s\n",
- gimp_object_get_name (best_file_proc));
-
- return best_file_proc;
- }
- }
-
- if (size_match_count == 1)
- return size_matched_proc;
-
- /* As a last resort, try matching by name, not skipping magic procs */
- file_proc = file_proc_find_by_name (procs, file, FALSE);
-
- if (file_proc)
- {
- /* we found a procedure, clear error that might have been set */
- g_clear_error (error);
- }
- else
- {
- /* set an error message unless one was already set */
- if (error && *error == NULL)
- g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
- _("Unknown file type"));
- }
-
- return file_proc;
-}
-
-GimpPlugInProcedure *
-file_procedure_find_by_prefix (GSList *procs,
- GFile *file)
-{
- g_return_val_if_fail (G_IS_FILE (file), NULL);
-
- return file_proc_find_by_prefix (procs, file, FALSE);
-}
-
-GimpPlugInProcedure *
-file_procedure_find_by_extension (GSList *procs,
- GFile *file)
-{
- g_return_val_if_fail (G_IS_FILE (file), NULL);
-
- return file_proc_find_by_extension (procs, file, FALSE);
-}
-
-GimpPlugInProcedure *
-file_procedure_find_by_mime_type (GSList *procs,
- const gchar *mime_type)
-{
- GSList *list;
-
- g_return_val_if_fail (mime_type != NULL, NULL);
-
- for (list = procs; list; list = g_slist_next (list))
- {
- GimpPlugInProcedure *proc = list->data;
- GSList *mime;
-
- for (mime = proc->mime_types_list; mime; mime = g_slist_next (mime))
- {
- if (! strcmp (mime_type, mime->data))
- return proc;
- }
- }
-
- return NULL;
-}
-
-
-/* private functions */
-
-static GimpPlugInProcedure *
-file_proc_find_by_prefix (GSList *procs,
- GFile *file,
- gboolean skip_magic)
-{
- gchar *uri = g_file_get_uri (file);
- GSList *p;
-
- for (p = procs; p; p = g_slist_next (p))
- {
- GimpPlugInProcedure *proc = p->data;
- GSList *prefixes;
-
- if (skip_magic && proc->magics_list)
- continue;
-
- for (prefixes = proc->prefixes_list;
- prefixes;
- prefixes = g_slist_next (prefixes))
- {
- if (g_str_has_prefix (uri, prefixes->data))
- {
- g_free (uri);
- return proc;
- }
- }
- }
-
- g_free (uri);
-
- return NULL;
-}
-
-static GimpPlugInProcedure *
-file_proc_find_by_extension (GSList *procs,
- GFile *file,
- gboolean skip_magic)
-{
- gchar *ext = gimp_file_get_extension (file);
-
- if (ext)
- {
- GSList *p;
-
- for (p = procs; p; p = g_slist_next (p))
- {
- GimpPlugInProcedure *proc = p->data;
-
- if (skip_magic && proc->magics_list)
- continue;
-
- if (g_slist_find_custom (proc->extensions_list,
- ext + 1,
- (GCompareFunc) g_ascii_strcasecmp))
- {
- g_free (ext);
-
- return proc;
- }
- }
-
- g_free (ext);
- }
-
- return NULL;
-}
-
-static GimpPlugInProcedure *
-file_proc_find_by_name (GSList *procs,
- GFile *file,
- gboolean skip_magic)
-{
- GimpPlugInProcedure *proc;
-
- proc = file_proc_find_by_prefix (procs, file, skip_magic);
-
- if (! proc)
- proc = file_proc_find_by_extension (procs, file, skip_magic);
-
- return proc;
-}
-
-static void
-file_convert_string (const gchar *instr,
- gchar *outmem,
- gint maxmem,
- gint *nmem)
-{
- /* Convert a string in C-notation to array of char */
- const guchar *uin = (const guchar *) instr;
- guchar *uout = (guchar *) outmem;
- guchar tmp[5], *tmpptr;
- guint k;
-
- while ((*uin != '\0') && ((((gchar *) uout) - outmem) < maxmem))
- {
- if (*uin != '\\') /* Not an escaped character ? */
- {
- *(uout++) = *(uin++);
- continue;
- }
-
- if (*(++uin) == '\0')
- {
- *(uout++) = '\\';
- break;
- }
-
- switch (*uin)
- {
- case '0': case '1': case '2': case '3': /* octal */
- for (tmpptr = tmp; (tmpptr - tmp) <= 3;)
- {
- *(tmpptr++) = *(uin++);
- if ( (*uin == '\0') || (!g_ascii_isdigit (*uin))
- || (*uin == '8') || (*uin == '9'))
- break;
- }
-
- *tmpptr = '\0';
- sscanf ((gchar *) tmp, "%o", &k);
- *(uout++) = k;
- break;
-
- case 'a': *(uout++) = '\a'; uin++; break;
- case 'b': *(uout++) = '\b'; uin++; break;
- case 't': *(uout++) = '\t'; uin++; break;
- case 'n': *(uout++) = '\n'; uin++; break;
- case 'v': *(uout++) = '\v'; uin++; break;
- case 'f': *(uout++) = '\f'; uin++; break;
- case 'r': *(uout++) = '\r'; uin++; break;
-
- default : *(uout++) = *(uin++); break;
- }
- }
-
- *nmem = ((gchar *) uout) - outmem;
-}
-
-static FileMatchType
-file_check_single_magic (const gchar *offset,
- const gchar *type,
- const gchar *value,
- const guchar *file_head,
- gint headsize,
- GFile *file,
- GInputStream *input)
-
-{
- FileMatchType found = FILE_MATCH_NONE;
- glong offs;
- gulong num_testval;
- gulong num_operator_val;
- gint numbytes, k;
- const gchar *num_operator_ptr;
- gchar num_operator;
-
- /* Check offset */
- if (sscanf (offset, "%ld", &offs) != 1)
- return FILE_MATCH_NONE;
-
- /* Check type of test */
- num_operator_ptr = NULL;
- num_operator = '\0';
-
- if (g_str_has_prefix (type, "byte"))
- {
- numbytes = 1;
- num_operator_ptr = type + strlen ("byte");
- }
- else if (g_str_has_prefix (type, "short"))
- {
- numbytes = 2;
- num_operator_ptr = type + strlen ("short");
- }
- else if (g_str_has_prefix (type, "long"))
- {
- numbytes = 4;
- num_operator_ptr = type + strlen ("long");
- }
- else if (g_str_has_prefix (type, "size"))
- {
- numbytes = 5;
- }
- else if (strcmp (type, "string") == 0)
- {
- numbytes = 0;
- }
- else
- {
- return FILE_MATCH_NONE;
- }
-
- /* Check numerical operator value if present */
- if (num_operator_ptr && (*num_operator_ptr == '&'))
- {
- if (g_ascii_isdigit (num_operator_ptr[1]))
- {
- if (num_operator_ptr[1] != '0') /* decimal */
- sscanf (num_operator_ptr+1, "%lu", &num_operator_val);
- else if (num_operator_ptr[2] == 'x') /* hexadecimal */
- sscanf (num_operator_ptr+3, "%lx", &num_operator_val);
- else /* octal */
- sscanf (num_operator_ptr+2, "%lo", &num_operator_val);
-
- num_operator = *num_operator_ptr;
- }
- }
-
- if (numbytes > 0)
- {
- /* Numerical test */
-
- gchar num_test = '=';
- gulong fileval = 0;
-
- /* Check test value */
- if ((value[0] == '>') || (value[0] == '<'))
- {
- num_test = value[0];
- value++;
- }
-
- errno = 0;
- num_testval = strtol (value, NULL, 0);
-
- if (errno != 0)
- return FILE_MATCH_NONE;
-
- if (numbytes == 5)
- {
- /* Check for file size */
-
- GFileInfo *info = g_file_query_info (file,
- G_FILE_ATTRIBUTE_STANDARD_SIZE,
- G_FILE_QUERY_INFO_NONE,
- NULL, NULL);
- if (! info)
- return FILE_MATCH_NONE;
-
- fileval = g_file_info_get_size (info);
- g_object_unref (info);
- }
- else if (offs >= 0 &&
- (offs + numbytes <= headsize))
- {
- /* We have it in memory */
-
- for (k = 0; k < numbytes; k++)
- fileval = (fileval << 8) | (glong) file_head[offs + k];
- }
- else
- {
- /* Read it from file */
-
- if (! g_seekable_seek (G_SEEKABLE (input), offs,
- (offs >= 0) ? G_SEEK_SET : G_SEEK_END,
- NULL, NULL))
- return FILE_MATCH_NONE;
-
- for (k = 0; k < numbytes; k++)
- {
- guchar byte;
- GError *error = NULL;
-
- byte = g_data_input_stream_read_byte (G_DATA_INPUT_STREAM (input),
- NULL, &error);
- if (error)
- {
- g_clear_error (&error);
- return FILE_MATCH_NONE;
- }
-
- fileval = (fileval << 8) | byte;
- }
- }
-
- if (num_operator == '&')
- fileval &= num_operator_val;
-
- if (num_test == '<')
- {
- if (fileval < num_testval)
- found = numbytes;
- }
- else if (num_test == '>')
- {
- if (fileval > num_testval)
- found = numbytes;
- }
- else
- {
- if (fileval == num_testval)
- found = numbytes;
- }
-
- if (found && (numbytes == 5))
- found = FILE_MATCH_SIZE;
- }
- else if (numbytes == 0)
- {
- /* String test */
-
- gchar mem_testval[256];
-
- file_convert_string (value,
- mem_testval, sizeof (mem_testval),
- &numbytes);
-
- if (numbytes <= 0)
- return FILE_MATCH_NONE;
-
- if (offs >= 0 &&
- (offs + numbytes <= headsize))
- {
- /* We have it in memory */
-
- if (memcmp (mem_testval, file_head + offs, numbytes) == 0)
- found = numbytes;
- }
- else
- {
- /* Read it from file */
-
- if (! g_seekable_seek (G_SEEKABLE (input), offs,
- (offs >= 0) ? G_SEEK_SET : G_SEEK_END,
- NULL, NULL))
- return FILE_MATCH_NONE;
-
- for (k = 0; k < numbytes; k++)
- {
- guchar byte;
- GError *error = NULL;
-
- byte = g_data_input_stream_read_byte (G_DATA_INPUT_STREAM (input),
- NULL, &error);
- if (error)
- {
- g_clear_error (&error);
-
- return FILE_MATCH_NONE;
- }
-
- if (byte != mem_testval[k])
- return FILE_MATCH_NONE;
- }
-
- found = numbytes;
- }
- }
-
- return found;
-}
-
-static FileMatchType
-file_check_magic_list (GSList *magics_list,
- const guchar *head,
- gint headsize,
- GFile *file,
- GInputStream *input)
-
-{
- gboolean and = FALSE;
- gboolean found = FALSE;
- FileMatchType best_match_val = FILE_MATCH_NONE;
- FileMatchType match_val = FILE_MATCH_NONE;
-
- for (; magics_list; magics_list = magics_list->next)
- {
- const gchar *offset;
- const gchar *type;
- const gchar *value;
- FileMatchType single_match_val = FILE_MATCH_NONE;
-
- if ((offset = magics_list->data) == NULL) return FILE_MATCH_NONE;
- if ((magics_list = magics_list->next) == NULL) return FILE_MATCH_NONE;
- if ((type = magics_list->data) == NULL) return FILE_MATCH_NONE;
- if ((magics_list = magics_list->next) == NULL) return FILE_MATCH_NONE;
- if ((value = magics_list->data) == NULL) return FILE_MATCH_NONE;
-
- single_match_val = file_check_single_magic (offset, type, value,
- head, headsize,
- file, input);
-
- if (and)
- found = found && (single_match_val != FILE_MATCH_NONE);
- else
- found = (single_match_val != FILE_MATCH_NONE);
-
- if (found)
- {
- if (match_val == FILE_MATCH_NONE)
- {
- /* if we have no match yet, this is it in any case */
-
- match_val = single_match_val;
- }
- else if (single_match_val != FILE_MATCH_NONE)
- {
- /* else if we have a match on this one, combine it with the
- * existing return value
- */
-
- if (single_match_val == FILE_MATCH_SIZE)
- {
- /* if we already have a magic match, simply increase
- * that by one to indicate "better match", not perfect
- * but better than losing the additional size match
- * entirely
- */
- if (match_val != FILE_MATCH_SIZE)
- match_val += 1;
- }
- else
- {
- /* if we already have a magic match, simply add to its
- * length; otherwise if we already have a size match,
- * combine it with this match, see comment above
- */
- if (match_val != FILE_MATCH_SIZE)
- match_val += single_match_val;
- else
- match_val = single_match_val + 1;
- }
- }
- }
- else
- {
- match_val = FILE_MATCH_NONE;
- }
-
- and = (strchr (offset, '&') != NULL);
-
- if (! and)
- {
- /* when done with this 'and' list, update best_match_val */
-
- if (best_match_val == FILE_MATCH_NONE)
- {
- /* if we have no best match yet, this is it */
-
- best_match_val = match_val;
- }
- else if (match_val != FILE_MATCH_NONE)
- {
- /* otherwise if this was a match, update the best match, note
- * that by using MAX we will not overwrite a magic match
- * with a size match
- */
-
- best_match_val = MAX (best_match_val, match_val);
- }
-
- match_val = FILE_MATCH_NONE;
- }
- }
-
- return best_match_val;
-}
diff --git a/app/plug-in/gimppluginmanager-file-procedure.h b/app/plug-in/gimppluginmanager-file-procedure.h
deleted file mode 100644
index 3508551966..0000000000
--- a/app/plug-in/gimppluginmanager-file-procedure.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* GIMP - The GNU Image Manipulation Program
- * Copyright (C) 1995 Spencer Kimball and Peter Mattis
- *
- * gimppluginmanager-file-procedure.h
- *
- * 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 3 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, see .
- */
-
-#ifndef __GIMP_PLUG_IN_MANAGER_FILE_PROCEDURE_H__
-#define __GIMP_PLUG_IN_MANAGER_FILE_PROCEDURE_H__
-
-
-
-GimpPlugInProcedure *file_procedure_find (GSList *procs,
- GFile *file,
- GError **error);
-GimpPlugInProcedure *file_procedure_find_by_prefix (GSList *procs,
- GFile *file);
-GimpPlugInProcedure *file_procedure_find_by_extension (GSList *procs,
- GFile *file);
-
-GimpPlugInProcedure *file_procedure_find_by_mime_type (GSList *procs,
- const gchar *mime_type);
-
-
-#endif /* __GIMP_PLUG_IN_MANAGER_FILE_PROCEDURE_H__ */
diff --git a/app/plug-in/gimppluginmanager-file.c b/app/plug-in/gimppluginmanager-file.c
index 25b78901aa..cee24d4cc6 100644
--- a/app/plug-in/gimppluginmanager-file.c
+++ b/app/plug-in/gimppluginmanager-file.c
@@ -19,6 +19,7 @@
#include "config.h"
+#include
#include
#include
@@ -29,14 +30,62 @@
#include "plug-in-types.h"
#include "core/gimp.h"
+#include "core/gimp-utils.h"
#include "gimpplugin.h"
#include "gimpplugindef.h"
#include "gimppluginmanager.h"
#include "gimppluginmanager-file.h"
-#include "gimppluginmanager-file-procedure.h"
#include "gimppluginprocedure.h"
+#include "gimp-log.h"
+
+#include "gimp-intl.h"
+
+
+typedef enum
+{
+ /* positive values indicate the length of a matching magic */
+
+ FILE_MATCH_NONE = 0,
+ FILE_MATCH_SIZE = -1
+} FileMatchType;
+
+
+/* local function prototypes */
+
+static GimpPlugInProcedure * file_procedure_find (GSList *procs,
+ GFile *file,
+ GError **error);
+static GimpPlugInProcedure * file_proc_find_by_prefix (GSList *procs,
+ GFile *file,
+ gboolean skip_magic);
+static GimpPlugInProcedure * file_proc_find_by_extension (GSList *procs,
+ GFile *file,
+ gboolean skip_magic);
+static GimpPlugInProcedure * file_proc_find_by_mime_type (GSList *procs,
+ const gchar *mime_type);
+static GimpPlugInProcedure * file_proc_find_by_name (GSList *procs,
+ GFile *file,
+ gboolean skip_magic);
+
+static void file_convert_string (const gchar *instr,
+ gchar *outmem,
+ gint maxmem,
+ gint *nmem);
+static FileMatchType file_check_single_magic (const gchar *offset,
+ const gchar *type,
+ const gchar *value,
+ const guchar *file_head,
+ gint headsize,
+ GFile *file,
+ GInputStream *input);
+static FileMatchType file_check_magic_list (GSList *magics_list,
+ const guchar *head,
+ gint headsize,
+ GFile *file,
+ GInputStream *input);
+
/* public functions */
@@ -102,13 +151,13 @@ gimp_plug_in_manager_file_procedure_find_by_prefix (GimpPlugInManager *mana
switch (group)
{
case GIMP_FILE_PROCEDURE_GROUP_OPEN:
- return file_procedure_find_by_prefix (manager->load_procs, file);
+ return file_proc_find_by_prefix (manager->load_procs, file, FALSE);
case GIMP_FILE_PROCEDURE_GROUP_SAVE:
- return file_procedure_find_by_prefix (manager->save_procs, file);
+ return file_proc_find_by_prefix (manager->save_procs, file, FALSE);
case GIMP_FILE_PROCEDURE_GROUP_EXPORT:
- return file_procedure_find_by_prefix (manager->export_procs, file);
+ return file_proc_find_by_prefix (manager->export_procs, file, FALSE);
default:
g_return_val_if_reached (NULL);
@@ -126,13 +175,13 @@ gimp_plug_in_manager_file_procedure_find_by_extension (GimpPlugInManager *m
switch (group)
{
case GIMP_FILE_PROCEDURE_GROUP_OPEN:
- return file_procedure_find_by_extension (manager->load_procs, file);
+ return file_proc_find_by_extension (manager->load_procs, file, FALSE);
case GIMP_FILE_PROCEDURE_GROUP_SAVE:
- return file_procedure_find_by_extension (manager->save_procs, file);
+ return file_proc_find_by_extension (manager->save_procs, file, FALSE);
case GIMP_FILE_PROCEDURE_GROUP_EXPORT:
- return file_procedure_find_by_extension (manager->export_procs, file);
+ return file_proc_find_by_extension (manager->export_procs, file, FALSE);
default:
g_return_val_if_reached (NULL);
@@ -150,15 +199,631 @@ gimp_plug_in_manager_file_procedure_find_by_mime_type (GimpPlugInManager *m
switch (group)
{
case GIMP_FILE_PROCEDURE_GROUP_OPEN:
- return file_procedure_find_by_mime_type (manager->load_procs, mime_type);
+ return file_proc_find_by_mime_type (manager->load_procs, mime_type);
case GIMP_FILE_PROCEDURE_GROUP_SAVE:
- return file_procedure_find_by_mime_type (manager->save_procs, mime_type);
+ return file_proc_find_by_mime_type (manager->save_procs, mime_type);
case GIMP_FILE_PROCEDURE_GROUP_EXPORT:
- return file_procedure_find_by_mime_type (manager->export_procs, mime_type);
+ return file_proc_find_by_mime_type (manager->export_procs, mime_type);
default:
g_return_val_if_reached (NULL);
}
}
+
+
+/* private functions */
+
+static GimpPlugInProcedure *
+file_procedure_find (GSList *procs,
+ GFile *file,
+ GError **error)
+{
+ GimpPlugInProcedure *file_proc;
+ GimpPlugInProcedure *size_matched_proc = NULL;
+ gint size_match_count = 0;
+
+ g_return_val_if_fail (procs != NULL, NULL);
+ g_return_val_if_fail (G_IS_FILE (file), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ /* First, check magicless prefixes/suffixes */
+ file_proc = file_proc_find_by_name (procs, file, TRUE);
+
+ if (file_proc)
+ return file_proc;
+
+ /* Then look for magics, but not on remote files */
+ if (g_file_is_native (file))
+ {
+ GSList *list;
+ GInputStream *input = NULL;
+ gboolean opened = FALSE;
+ gsize head_size = 0;
+ guchar head[256];
+ FileMatchType best_match_val = FILE_MATCH_NONE;
+ GimpPlugInProcedure *best_file_proc = NULL;
+
+ for (list = procs; list; list = g_slist_next (list))
+ {
+ file_proc = list->data;
+
+ if (file_proc->magics_list)
+ {
+ if (G_UNLIKELY (! opened))
+ {
+ input = G_INPUT_STREAM (g_file_read (file, NULL, error));
+
+ if (input)
+ {
+ g_input_stream_read_all (input,
+ head, sizeof (head),
+ &head_size, NULL, error);
+
+ if (head_size < 4)
+ {
+ g_object_unref (input);
+ input = NULL;
+ }
+ else
+ {
+ GDataInputStream *data_input;
+
+ data_input = g_data_input_stream_new (input);
+ g_object_unref (input);
+ input = G_INPUT_STREAM (data_input);
+ }
+ }
+
+ opened = TRUE;
+ }
+
+ if (head_size >= 4)
+ {
+ FileMatchType match_val;
+
+ match_val = file_check_magic_list (file_proc->magics_list,
+ head, head_size,
+ file, input);
+
+ if (match_val == FILE_MATCH_SIZE)
+ {
+ /* Use it only if no other magic matches */
+ size_match_count++;
+ size_matched_proc = file_proc;
+ }
+ else if (match_val != FILE_MATCH_NONE)
+ {
+ GIMP_LOG (MAGIC_MATCH,
+ "magic match %d on %s\n",
+ match_val,
+ gimp_object_get_name (file_proc));
+
+ if (match_val > best_match_val)
+ {
+ best_match_val = match_val;
+ best_file_proc = file_proc;
+ }
+ }
+ }
+ }
+ }
+
+ if (input)
+ g_object_unref (input);
+
+ if (best_file_proc)
+ {
+ GIMP_LOG (MAGIC_MATCH,
+ "best magic match on %s\n",
+ gimp_object_get_name (best_file_proc));
+
+ return best_file_proc;
+ }
+ }
+
+ if (size_match_count == 1)
+ return size_matched_proc;
+
+ /* As a last resort, try matching by name, not skipping magic procs */
+ file_proc = file_proc_find_by_name (procs, file, FALSE);
+
+ if (file_proc)
+ {
+ /* we found a procedure, clear error that might have been set */
+ g_clear_error (error);
+ }
+ else
+ {
+ /* set an error message unless one was already set */
+ if (error && *error == NULL)
+ g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unknown file type"));
+ }
+
+ return file_proc;
+}
+
+static GimpPlugInProcedure *
+file_proc_find_by_mime_type (GSList *procs,
+ const gchar *mime_type)
+{
+ GSList *list;
+
+ g_return_val_if_fail (mime_type != NULL, NULL);
+
+ for (list = procs; list; list = g_slist_next (list))
+ {
+ GimpPlugInProcedure *proc = list->data;
+ GSList *mime;
+
+ for (mime = proc->mime_types_list; mime; mime = g_slist_next (mime))
+ {
+ if (! strcmp (mime_type, mime->data))
+ return proc;
+ }
+ }
+
+ return NULL;
+}
+
+static GimpPlugInProcedure *
+file_proc_find_by_prefix (GSList *procs,
+ GFile *file,
+ gboolean skip_magic)
+{
+ gchar *uri = g_file_get_uri (file);
+ GSList *p;
+
+ for (p = procs; p; p = g_slist_next (p))
+ {
+ GimpPlugInProcedure *proc = p->data;
+ GSList *prefixes;
+
+ if (skip_magic && proc->magics_list)
+ continue;
+
+ for (prefixes = proc->prefixes_list;
+ prefixes;
+ prefixes = g_slist_next (prefixes))
+ {
+ if (g_str_has_prefix (uri, prefixes->data))
+ {
+ g_free (uri);
+ return proc;
+ }
+ }
+ }
+
+ g_free (uri);
+
+ return NULL;
+}
+
+static GimpPlugInProcedure *
+file_proc_find_by_extension (GSList *procs,
+ GFile *file,
+ gboolean skip_magic)
+{
+ gchar *ext = gimp_file_get_extension (file);
+
+ if (ext)
+ {
+ GSList *p;
+
+ for (p = procs; p; p = g_slist_next (p))
+ {
+ GimpPlugInProcedure *proc = p->data;
+
+ if (skip_magic && proc->magics_list)
+ continue;
+
+ if (g_slist_find_custom (proc->extensions_list,
+ ext + 1,
+ (GCompareFunc) g_ascii_strcasecmp))
+ {
+ g_free (ext);
+
+ return proc;
+ }
+ }
+
+ g_free (ext);
+ }
+
+ return NULL;
+}
+
+static GimpPlugInProcedure *
+file_proc_find_by_name (GSList *procs,
+ GFile *file,
+ gboolean skip_magic)
+{
+ GimpPlugInProcedure *proc;
+
+ proc = file_proc_find_by_prefix (procs, file, skip_magic);
+
+ if (! proc)
+ proc = file_proc_find_by_extension (procs, file, skip_magic);
+
+ return proc;
+}
+
+static void
+file_convert_string (const gchar *instr,
+ gchar *outmem,
+ gint maxmem,
+ gint *nmem)
+{
+ /* Convert a string in C-notation to array of char */
+ const guchar *uin = (const guchar *) instr;
+ guchar *uout = (guchar *) outmem;
+ guchar tmp[5], *tmpptr;
+ guint k;
+
+ while ((*uin != '\0') && ((((gchar *) uout) - outmem) < maxmem))
+ {
+ if (*uin != '\\') /* Not an escaped character ? */
+ {
+ *(uout++) = *(uin++);
+ continue;
+ }
+
+ if (*(++uin) == '\0')
+ {
+ *(uout++) = '\\';
+ break;
+ }
+
+ switch (*uin)
+ {
+ case '0': case '1': case '2': case '3': /* octal */
+ for (tmpptr = tmp; (tmpptr - tmp) <= 3;)
+ {
+ *(tmpptr++) = *(uin++);
+ if ( (*uin == '\0') || (!g_ascii_isdigit (*uin))
+ || (*uin == '8') || (*uin == '9'))
+ break;
+ }
+
+ *tmpptr = '\0';
+ sscanf ((gchar *) tmp, "%o", &k);
+ *(uout++) = k;
+ break;
+
+ case 'a': *(uout++) = '\a'; uin++; break;
+ case 'b': *(uout++) = '\b'; uin++; break;
+ case 't': *(uout++) = '\t'; uin++; break;
+ case 'n': *(uout++) = '\n'; uin++; break;
+ case 'v': *(uout++) = '\v'; uin++; break;
+ case 'f': *(uout++) = '\f'; uin++; break;
+ case 'r': *(uout++) = '\r'; uin++; break;
+
+ default : *(uout++) = *(uin++); break;
+ }
+ }
+
+ *nmem = ((gchar *) uout) - outmem;
+}
+
+static FileMatchType
+file_check_single_magic (const gchar *offset,
+ const gchar *type,
+ const gchar *value,
+ const guchar *file_head,
+ gint headsize,
+ GFile *file,
+ GInputStream *input)
+
+{
+ FileMatchType found = FILE_MATCH_NONE;
+ glong offs;
+ gulong num_testval;
+ gulong num_operator_val;
+ gint numbytes, k;
+ const gchar *num_operator_ptr;
+ gchar num_operator;
+
+ /* Check offset */
+ if (sscanf (offset, "%ld", &offs) != 1)
+ return FILE_MATCH_NONE;
+
+ /* Check type of test */
+ num_operator_ptr = NULL;
+ num_operator = '\0';
+
+ if (g_str_has_prefix (type, "byte"))
+ {
+ numbytes = 1;
+ num_operator_ptr = type + strlen ("byte");
+ }
+ else if (g_str_has_prefix (type, "short"))
+ {
+ numbytes = 2;
+ num_operator_ptr = type + strlen ("short");
+ }
+ else if (g_str_has_prefix (type, "long"))
+ {
+ numbytes = 4;
+ num_operator_ptr = type + strlen ("long");
+ }
+ else if (g_str_has_prefix (type, "size"))
+ {
+ numbytes = 5;
+ }
+ else if (strcmp (type, "string") == 0)
+ {
+ numbytes = 0;
+ }
+ else
+ {
+ return FILE_MATCH_NONE;
+ }
+
+ /* Check numerical operator value if present */
+ if (num_operator_ptr && (*num_operator_ptr == '&'))
+ {
+ if (g_ascii_isdigit (num_operator_ptr[1]))
+ {
+ if (num_operator_ptr[1] != '0') /* decimal */
+ sscanf (num_operator_ptr+1, "%lu", &num_operator_val);
+ else if (num_operator_ptr[2] == 'x') /* hexadecimal */
+ sscanf (num_operator_ptr+3, "%lx", &num_operator_val);
+ else /* octal */
+ sscanf (num_operator_ptr+2, "%lo", &num_operator_val);
+
+ num_operator = *num_operator_ptr;
+ }
+ }
+
+ if (numbytes > 0)
+ {
+ /* Numerical test */
+
+ gchar num_test = '=';
+ gulong fileval = 0;
+
+ /* Check test value */
+ if ((value[0] == '>') || (value[0] == '<'))
+ {
+ num_test = value[0];
+ value++;
+ }
+
+ errno = 0;
+ num_testval = strtol (value, NULL, 0);
+
+ if (errno != 0)
+ return FILE_MATCH_NONE;
+
+ if (numbytes == 5)
+ {
+ /* Check for file size */
+
+ GFileInfo *info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_SIZE,
+ G_FILE_QUERY_INFO_NONE,
+ NULL, NULL);
+ if (! info)
+ return FILE_MATCH_NONE;
+
+ fileval = g_file_info_get_size (info);
+ g_object_unref (info);
+ }
+ else if (offs >= 0 &&
+ (offs + numbytes <= headsize))
+ {
+ /* We have it in memory */
+
+ for (k = 0; k < numbytes; k++)
+ fileval = (fileval << 8) | (glong) file_head[offs + k];
+ }
+ else
+ {
+ /* Read it from file */
+
+ if (! g_seekable_seek (G_SEEKABLE (input), offs,
+ (offs >= 0) ? G_SEEK_SET : G_SEEK_END,
+ NULL, NULL))
+ return FILE_MATCH_NONE;
+
+ for (k = 0; k < numbytes; k++)
+ {
+ guchar byte;
+ GError *error = NULL;
+
+ byte = g_data_input_stream_read_byte (G_DATA_INPUT_STREAM (input),
+ NULL, &error);
+ if (error)
+ {
+ g_clear_error (&error);
+ return FILE_MATCH_NONE;
+ }
+
+ fileval = (fileval << 8) | byte;
+ }
+ }
+
+ if (num_operator == '&')
+ fileval &= num_operator_val;
+
+ if (num_test == '<')
+ {
+ if (fileval < num_testval)
+ found = numbytes;
+ }
+ else if (num_test == '>')
+ {
+ if (fileval > num_testval)
+ found = numbytes;
+ }
+ else
+ {
+ if (fileval == num_testval)
+ found = numbytes;
+ }
+
+ if (found && (numbytes == 5))
+ found = FILE_MATCH_SIZE;
+ }
+ else if (numbytes == 0)
+ {
+ /* String test */
+
+ gchar mem_testval[256];
+
+ file_convert_string (value,
+ mem_testval, sizeof (mem_testval),
+ &numbytes);
+
+ if (numbytes <= 0)
+ return FILE_MATCH_NONE;
+
+ if (offs >= 0 &&
+ (offs + numbytes <= headsize))
+ {
+ /* We have it in memory */
+
+ if (memcmp (mem_testval, file_head + offs, numbytes) == 0)
+ found = numbytes;
+ }
+ else
+ {
+ /* Read it from file */
+
+ if (! g_seekable_seek (G_SEEKABLE (input), offs,
+ (offs >= 0) ? G_SEEK_SET : G_SEEK_END,
+ NULL, NULL))
+ return FILE_MATCH_NONE;
+
+ for (k = 0; k < numbytes; k++)
+ {
+ guchar byte;
+ GError *error = NULL;
+
+ byte = g_data_input_stream_read_byte (G_DATA_INPUT_STREAM (input),
+ NULL, &error);
+ if (error)
+ {
+ g_clear_error (&error);
+
+ return FILE_MATCH_NONE;
+ }
+
+ if (byte != mem_testval[k])
+ return FILE_MATCH_NONE;
+ }
+
+ found = numbytes;
+ }
+ }
+
+ return found;
+}
+
+static FileMatchType
+file_check_magic_list (GSList *magics_list,
+ const guchar *head,
+ gint headsize,
+ GFile *file,
+ GInputStream *input)
+
+{
+ gboolean and = FALSE;
+ gboolean found = FALSE;
+ FileMatchType best_match_val = FILE_MATCH_NONE;
+ FileMatchType match_val = FILE_MATCH_NONE;
+
+ for (; magics_list; magics_list = magics_list->next)
+ {
+ const gchar *offset;
+ const gchar *type;
+ const gchar *value;
+ FileMatchType single_match_val = FILE_MATCH_NONE;
+
+ if ((offset = magics_list->data) == NULL) return FILE_MATCH_NONE;
+ if ((magics_list = magics_list->next) == NULL) return FILE_MATCH_NONE;
+ if ((type = magics_list->data) == NULL) return FILE_MATCH_NONE;
+ if ((magics_list = magics_list->next) == NULL) return FILE_MATCH_NONE;
+ if ((value = magics_list->data) == NULL) return FILE_MATCH_NONE;
+
+ single_match_val = file_check_single_magic (offset, type, value,
+ head, headsize,
+ file, input);
+
+ if (and)
+ found = found && (single_match_val != FILE_MATCH_NONE);
+ else
+ found = (single_match_val != FILE_MATCH_NONE);
+
+ if (found)
+ {
+ if (match_val == FILE_MATCH_NONE)
+ {
+ /* if we have no match yet, this is it in any case */
+
+ match_val = single_match_val;
+ }
+ else if (single_match_val != FILE_MATCH_NONE)
+ {
+ /* else if we have a match on this one, combine it with the
+ * existing return value
+ */
+
+ if (single_match_val == FILE_MATCH_SIZE)
+ {
+ /* if we already have a magic match, simply increase
+ * that by one to indicate "better match", not perfect
+ * but better than losing the additional size match
+ * entirely
+ */
+ if (match_val != FILE_MATCH_SIZE)
+ match_val += 1;
+ }
+ else
+ {
+ /* if we already have a magic match, simply add to its
+ * length; otherwise if we already have a size match,
+ * combine it with this match, see comment above
+ */
+ if (match_val != FILE_MATCH_SIZE)
+ match_val += single_match_val;
+ else
+ match_val = single_match_val + 1;
+ }
+ }
+ }
+ else
+ {
+ match_val = FILE_MATCH_NONE;
+ }
+
+ and = (strchr (offset, '&') != NULL);
+
+ if (! and)
+ {
+ /* when done with this 'and' list, update best_match_val */
+
+ if (best_match_val == FILE_MATCH_NONE)
+ {
+ /* if we have no best match yet, this is it */
+
+ best_match_val = match_val;
+ }
+ else if (match_val != FILE_MATCH_NONE)
+ {
+ /* otherwise if this was a match, update the best match, note
+ * that by using MAX we will not overwrite a magic match
+ * with a size match
+ */
+
+ best_match_val = MAX (best_match_val, match_val);
+ }
+
+ match_val = FILE_MATCH_NONE;
+ }
+ }
+
+ return best_match_val;
+}
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 4df3195412..dd8f42eb74 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -376,7 +376,7 @@ app/plug-in/gimpplugin-message.c
app/plug-in/gimpplugin-progress.c
app/plug-in/gimppluginmanager.c
app/plug-in/gimppluginmanager-call.c
-app/plug-in/gimppluginmanager-file-procedure.c
+app/plug-in/gimppluginmanager-file.c
app/plug-in/gimppluginmanager-restore.c
app/plug-in/gimppluginprocedure.c
app/plug-in/gimppluginprocframe.c