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