/* The GIMP -- an image manipulation program * Copyright (C) 1995, 1996, 1997 Spencer Kimball and Peter Mattis * Copyright (C) 1997 Josh MacDonald * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #include #include #include #ifdef HAVE_SYS_PARAM_H #include #endif #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include "libgimpmath/gimpmath.h" #include "core/core-types.h" #include "base/temp-buf.h" #include "core/gimp.h" #include "core/gimpimage.h" #include "pdb/procedural_db.h" #include "plug-in/plug-in.h" #include "plug-in/plug-in-proc.h" #include "file-utils.h" #include "gimp-intl.h" /* local function prototypes */ static PlugInProcDef * file_proc_find_by_prefix (GSList *procs, const gchar *uri, gboolean skip_magic); static PlugInProcDef * file_proc_find_by_extension (GSList *procs, const gchar *uri, gboolean skip_magic); static PlugInProcDef * file_proc_find_by_name (GSList *procs, const gchar *uri, gboolean skip_magic); static void file_convert_string (gchar *instr, gchar *outmem, gint maxmem, gint *nmem); static gint file_check_single_magic (gchar *offset, gchar *type, gchar *value, gint headsize, guchar *file_head, FILE *ifp); static gint file_check_magic_list (GSList *magics_list, gint headsize, guchar *head, FILE *ifp); /* public functions */ gchar * file_utils_filename_to_uri (GSList *procs, const gchar *filename, GError **error) { gchar *absolute; gchar *uri; g_return_val_if_fail (procs != NULL, NULL); g_return_val_if_fail (filename != NULL, NULL); /* check for prefixes like http or ftp */ if (file_proc_find_by_prefix (procs, filename, FALSE)) { if (g_utf8_validate (filename, -1, NULL)) { return g_strdup (filename); } else { g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE, _("Invalid character sequence in URI")); return NULL; } } if (! g_path_is_absolute (filename)) { gchar *current; current = g_get_current_dir (); absolute = g_build_filename (current, filename, NULL); g_free (current); } else { absolute = g_strdup (filename); } uri = g_filename_to_uri (absolute, NULL, error); g_free (absolute); return uri; } PlugInProcDef * file_utils_find_proc (GSList *procs, const gchar *uri) { PlugInProcDef *file_proc; GSList *all_procs = procs; gchar *filename; g_return_val_if_fail (procs != NULL, NULL); g_return_val_if_fail (uri != NULL, NULL); /* First, check magicless prefixes/suffixes */ file_proc = file_proc_find_by_name (all_procs, uri, TRUE); if (file_proc) return file_proc; filename = g_filename_from_uri (uri, NULL, NULL); /* Then look for magics */ if (filename) { PlugInProcDef *size_matched_proc = NULL; FILE *ifp = NULL; gint head_size = -2; gint size_match_count = 0; gint match_val; guchar head[256]; while (procs) { file_proc = procs->data; procs = procs->next; if (file_proc->magics_list) { if (head_size == -2) { head_size = 0; if ((ifp = fopen (filename, "rb")) != NULL) head_size = fread ((gchar *) head, 1, sizeof (head), ifp); } if (head_size >= 4) { match_val = file_check_magic_list (file_proc->magics_list, head_size, head, ifp); if (match_val == 2) /* size match ? */ { /* Use it only if no other magic matches */ size_match_count++; size_matched_proc = file_proc; } else if (match_val) { fclose (ifp); g_free (filename); return file_proc; } } } } if (ifp) fclose (ifp); g_free (filename); if (size_match_count == 1) return size_matched_proc; } /* As a last ditch, try matching by name */ return file_proc_find_by_name (all_procs, uri, FALSE); } gchar * file_utils_uri_to_utf8_basename (const gchar *uri) { gchar *filename; gchar *basename; g_assert (uri != NULL); g_return_val_if_fail (uri != NULL, NULL); filename = file_utils_uri_to_utf8_filename (uri); if (strstr (filename, G_DIR_SEPARATOR_S)) { basename = g_path_get_basename (filename); g_free (filename); return basename; } else if (strstr (filename, "://")) { basename = strrchr (uri, '/'); basename = g_strdup (basename + 1); g_free (filename); return basename; } return filename; } gchar * file_utils_uri_to_utf8_filename (const gchar *uri) { g_return_val_if_fail (uri != NULL, NULL); if (! strncmp (uri, "file:", strlen ("file:"))) { gchar *filename; filename = g_filename_from_uri (uri, NULL, NULL); if (filename) { gchar *utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL); g_free (filename); return utf8; } } return g_strdup (uri); } /* private functions */ static PlugInProcDef * file_proc_find_by_prefix (GSList *procs, const gchar *uri, gboolean skip_magic) { GSList *p; for (p = procs; p; p = g_slist_next (p)) { PlugInProcDef *proc = p->data; GSList *prefixes; if (skip_magic && proc->magics_list) continue; for (prefixes = proc->prefixes_list; prefixes; prefixes = g_slist_next (prefixes)) { if (strncmp (uri, prefixes->data, strlen (prefixes->data)) == 0) return proc; } } return NULL; } static PlugInProcDef * file_proc_find_by_extension (GSList *procs, const gchar *uri, gboolean skip_magic) { GSList *p; gchar *ext; ext = strrchr (uri, '.'); if (ext) ext++; for (p = procs; p; p = g_slist_next (p)) { PlugInProcDef *proc = p->data; GSList *extensions; for (extensions = proc->extensions_list; ext && extensions; extensions = g_slist_next (extensions)) { gchar *p1 = ext; gchar *p2 = (gchar *) extensions->data; if (skip_magic && proc->magics_list) continue; while (*p1 && *p2) { if (g_ascii_tolower (*p1) != g_ascii_tolower (*p2)) break; p1++; p2++; } if (!(*p1) && !(*p2)) return proc; } } return NULL; } static PlugInProcDef * file_proc_find_by_name (GSList *procs, const gchar *uri, gboolean skip_magic) { PlugInProcDef *proc; proc = file_proc_find_by_prefix (procs, uri, skip_magic); if (! proc) proc = file_proc_find_by_extension (procs, uri, skip_magic); return proc; } static void file_convert_string (gchar *instr, gchar *outmem, gint maxmem, gint *nmem) { /* Convert a string in C-notation to array of char */ guchar *uin = (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 gint file_check_single_magic (gchar *offset, gchar *type, gchar *value, gint headsize, guchar *file_head, FILE *ifp) { /* Return values are 0: no match, 1: magic match, 2: size match */ glong offs; gulong num_testval, num_operatorval; gulong fileval; gint numbytes, k, c = 0, found = 0; gchar *num_operator_ptr, num_operator, num_test; guchar mem_testval[256]; /* Check offset */ if (sscanf (offset, "%ld", &offs) != 1) return (0); if (offs < 0) return (0); /* Check type of test */ num_operator_ptr = NULL; num_operator = '\0'; num_test = '='; if (strncmp (type, "byte", 4) == 0) { numbytes = 1; num_operator_ptr = type+4; } else if (strncmp (type, "short", 5) == 0) { numbytes = 2; num_operator_ptr = type+5; } else if (strncmp (type, "long", 4) == 0) { numbytes = 4; num_operator_ptr = type+4; } else if (strncmp (type, "size", 4) == 0) { numbytes = 5; } else if (strcmp (type, "string") == 0) { numbytes = 0; } else return (0); /* 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_operatorval); else if (num_operator_ptr[2] == 'x') /* hexadecimal */ sscanf (num_operator_ptr+3, "%lx", &num_operatorval); else /* octal */ sscanf (num_operator_ptr+2, "%lo", &num_operatorval); num_operator = *num_operator_ptr; } } if (numbytes > 0) /* Numerical test ? */ { /* Check test value */ if ((value[0] == '=') || (value[0] == '>') || (value[0] == '<')) { num_test = value[0]; value++; } if (!g_ascii_isdigit (value[0])) return (0); /* * to anybody reading this: is strtol's parsing behaviour * (e.g. "0x" prefix) broken on some systems or why do we * do the base detection ourselves? * */ if (value[0] != '0') /* decimal */ num_testval = strtol(value, NULL, 10); else if (value[1] == 'x') /* hexadecimal */ num_testval = (unsigned long)strtoul(value+2, NULL, 16); else /* octal */ num_testval = strtol(value+1, NULL, 8); fileval = 0; if (numbytes == 5) /* Check for file size ? */ { struct stat buf; if (fstat (fileno (ifp), &buf) < 0) return (0); fileval = buf.st_size; } else if (offs + numbytes <= headsize) /* We have it in memory ? */ { for (k = 0; k < numbytes; k++) fileval = (fileval << 8) | (long)file_head[offs+k]; } else /* Read it from file */ { if (fseek (ifp, offs, SEEK_SET) < 0) return (0); for (k = 0; k < numbytes; k++) fileval = (fileval << 8) | (c = getc (ifp)); if (c == EOF) return (0); } if (num_operator == '&') fileval &= num_operatorval; if (num_test == '<') found = (fileval < num_testval); else if (num_test == '>') found = (fileval > num_testval); else found = (fileval == num_testval); if (found && (numbytes == 5)) found = 2; } else if (numbytes == 0) /* String test */ { file_convert_string ((char *)value, (char *)mem_testval, sizeof (mem_testval), &numbytes); if (numbytes <= 0) return (0); if (offs + numbytes <= headsize) /* We have it in memory ? */ { found = (memcmp (mem_testval, file_head+offs, numbytes) == 0); } else /* Read it from file */ { if (fseek (ifp, offs, SEEK_SET) < 0) return (0); found = 1; for (k = 0; found && (k < numbytes); k++) { c = getc (ifp); found = (c != EOF) && (c == (int)mem_testval[k]); } } } return found; } /* * Return values are 0: no match, 1: magic match, 2: size match */ static gint file_check_magic_list (GSList *magics_list, gint headsize, guchar *head, FILE *ifp) { gchar *offset; gchar *type; gchar *value; gint and = 0; gint found = 0; gint match_val; while (magics_list) { if ((offset = (gchar *)magics_list->data) == NULL) break; if ((magics_list = magics_list->next) == NULL) break; if ((type = (gchar *)magics_list->data) == NULL) break; if ((magics_list = magics_list->next) == NULL) break; if ((value = (gchar *)magics_list->data) == NULL) break; magics_list = magics_list->next; match_val = file_check_single_magic (offset, type, value, headsize, head, ifp); if (and) found = found && match_val; else found = match_val; and = (strchr (offset, '&') != NULL); if ((!and) && found) return match_val; } return 0; }