In particular, if the shebang is `#!/usr/bin/env lang` and we have not
registered a specific interpreter for `lang`, the system should leave
the env tool search the right interpreter for us. We only bypass env
when we set our own explicit interpreter.
(cherry picked from commit 9e844ae1d7)
Slight modification from master commit: not applying to
palette-to-gradient.py plug-in.
875 lines
20 KiB
C
875 lines
20 KiB
C
/* GIMP - The GNU Image Manipulation Program
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
*
|
|
* gimpinterpreterdb.c
|
|
* (C) 2005 Manish Singh <yosh@gimp.org>
|
|
*
|
|
* 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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/*
|
|
* 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 <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <gio/gio.h>
|
|
|
|
#include "libgimpbase/gimpbase.h"
|
|
|
|
#include "plug-in-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_finalize (GObject *object);
|
|
|
|
static void gimp_interpreter_db_load_interp_file (GimpInterpreterDB *db,
|
|
GFile *file);
|
|
|
|
static void gimp_interpreter_db_add_program (GimpInterpreterDB *db,
|
|
GFile *file,
|
|
gchar *buffer);
|
|
static void gimp_interpreter_db_add_binfmt_misc (GimpInterpreterDB *db,
|
|
GFile *file,
|
|
gchar *buffer);
|
|
|
|
static gboolean gimp_interpreter_db_add_extension (GFile *file,
|
|
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);
|
|
|
|
|
|
G_DEFINE_TYPE (GimpInterpreterDB, gimp_interpreter_db, G_TYPE_OBJECT)
|
|
|
|
#define parent_class gimp_interpreter_db_parent_class
|
|
|
|
|
|
static void
|
|
gimp_interpreter_db_class_init (GimpInterpreterDBClass *class)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
|
|
|
object_class->finalize = gimp_interpreter_db_finalize;
|
|
}
|
|
|
|
static void
|
|
gimp_interpreter_db_init (GimpInterpreterDB *db)
|
|
{
|
|
}
|
|
|
|
static void
|
|
gimp_interpreter_db_finalize (GObject *object)
|
|
{
|
|
GimpInterpreterDB *db = GIMP_INTERPRETER_DB (object);
|
|
|
|
gimp_interpreter_db_clear (db);
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
}
|
|
|
|
GimpInterpreterDB *
|
|
gimp_interpreter_db_new (gboolean verbose)
|
|
{
|
|
GimpInterpreterDB *db = g_object_new (GIMP_TYPE_INTERPRETER_DB, NULL);
|
|
|
|
db->verbose = verbose;
|
|
|
|
return db;
|
|
}
|
|
|
|
void
|
|
gimp_interpreter_db_load (GimpInterpreterDB *db,
|
|
GList *path)
|
|
{
|
|
GList *list;
|
|
|
|
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);
|
|
|
|
for (list = path; list; list = g_list_next (list))
|
|
{
|
|
GFile *dir = list->data;
|
|
GFileEnumerator *enumerator;
|
|
|
|
enumerator =
|
|
g_file_enumerate_children (dir,
|
|
G_FILE_ATTRIBUTE_STANDARD_NAME ","
|
|
G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN ","
|
|
G_FILE_ATTRIBUTE_STANDARD_TYPE,
|
|
G_FILE_QUERY_INFO_NONE,
|
|
NULL, NULL);
|
|
|
|
if (enumerator)
|
|
{
|
|
GFileInfo *info;
|
|
|
|
while ((info = g_file_enumerator_next_file (enumerator,
|
|
NULL, NULL)))
|
|
{
|
|
if (! g_file_info_get_is_hidden (info) &&
|
|
g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR)
|
|
{
|
|
GFile *file = g_file_enumerator_get_child (enumerator, info);
|
|
|
|
gimp_interpreter_db_load_interp_file (db, file);
|
|
|
|
g_object_unref (file);
|
|
}
|
|
|
|
g_object_unref (info);
|
|
}
|
|
|
|
g_object_unref (enumerator);
|
|
}
|
|
}
|
|
|
|
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 (GimpInterpreterDB *db,
|
|
GFile *file)
|
|
{
|
|
GInputStream *input;
|
|
GDataInputStream *data_input;
|
|
gchar *buffer;
|
|
gsize buffer_len;
|
|
GError *error = NULL;
|
|
|
|
if (db->verbose)
|
|
g_print ("Parsing '%s'\n", gimp_file_get_utf8_name (file));
|
|
|
|
input = G_INPUT_STREAM (g_file_read (file, NULL, &error));
|
|
if (! input)
|
|
{
|
|
g_message (_("Could not open '%s' for reading: %s"),
|
|
gimp_file_get_utf8_name (file),
|
|
error->message);
|
|
g_clear_error (&error);
|
|
return;
|
|
}
|
|
|
|
data_input = g_data_input_stream_new (input);
|
|
g_object_unref (input);
|
|
|
|
while ((buffer = g_data_input_stream_read_line (data_input, &buffer_len,
|
|
NULL, &error)))
|
|
{
|
|
/* Skip comments */
|
|
if (buffer[0] == '#')
|
|
{
|
|
g_free (buffer);
|
|
continue;
|
|
}
|
|
|
|
if (g_ascii_isalnum (buffer[0]) || (buffer[0] == '/'))
|
|
{
|
|
gimp_interpreter_db_add_program (db, file, buffer);
|
|
}
|
|
else if (! g_ascii_isspace (buffer[0]) && (buffer[0] != '\0'))
|
|
{
|
|
gimp_interpreter_db_add_binfmt_misc (db, file, buffer);
|
|
}
|
|
|
|
g_free (buffer);
|
|
}
|
|
|
|
if (error)
|
|
{
|
|
g_message (_("Error reading '%s': %s"),
|
|
gimp_file_get_utf8_name (file),
|
|
error->message);
|
|
g_clear_error (&error);
|
|
}
|
|
|
|
g_object_unref (data_input);
|
|
}
|
|
|
|
static void
|
|
gimp_interpreter_db_add_program (GimpInterpreterDB *db,
|
|
GFile *file,
|
|
gchar *buffer)
|
|
{
|
|
gchar *name;
|
|
gchar *program;
|
|
gchar *p;
|
|
|
|
p = strchr (buffer, '=');
|
|
if (! p)
|
|
return;
|
|
|
|
*p = '\0';
|
|
|
|
name = buffer;
|
|
program = p + 1;
|
|
|
|
if (! g_file_test (program, G_FILE_TEST_IS_EXECUTABLE))
|
|
{
|
|
gchar *prog;
|
|
|
|
prog = g_find_program_in_path (program);
|
|
if (! prog || ! g_file_test (prog, G_FILE_TEST_IS_EXECUTABLE))
|
|
{
|
|
g_message (_("Bad interpreter referenced in interpreter file %s: %s"),
|
|
gimp_file_get_utf8_name (file),
|
|
gimp_filename_to_utf8 (program));
|
|
if (prog)
|
|
g_free (prog);
|
|
return;
|
|
}
|
|
program = prog;
|
|
}
|
|
else
|
|
program = g_strdup (program);
|
|
|
|
if (! g_hash_table_lookup (db->programs, name))
|
|
g_hash_table_insert (db->programs, g_strdup (name), program);
|
|
}
|
|
|
|
static void
|
|
gimp_interpreter_db_add_binfmt_misc (GimpInterpreterDB *db,
|
|
GFile *file,
|
|
gchar *buffer)
|
|
{
|
|
gchar **tokens = NULL;
|
|
gchar *name, *type, *program;
|
|
gsize count;
|
|
gchar del[2];
|
|
|
|
count = strlen (buffer);
|
|
|
|
if ((count < 10) || (count > 255))
|
|
goto bail;
|
|
|
|
buffer = g_strndup (buffer, count + 9);
|
|
|
|
del[0] = *buffer;
|
|
del[1] = '\0';
|
|
|
|
memset (buffer + count, del[0], 8);
|
|
|
|
tokens = g_strsplit (buffer + 1, del, -1);
|
|
|
|
g_free (buffer);
|
|
|
|
name = tokens[0];
|
|
type = tokens[1];
|
|
program = tokens[5];
|
|
|
|
if ((name[0] == '\0') || (program[0] == '\0') ||
|
|
(type[0] == '\0') || (type[1] != '\0'))
|
|
goto bail;
|
|
|
|
switch (type[0])
|
|
{
|
|
case 'E':
|
|
if (! gimp_interpreter_db_add_extension (file, 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_file_get_utf8_name (file));
|
|
|
|
out:
|
|
g_strfreev (tokens);
|
|
}
|
|
|
|
static gboolean
|
|
gimp_interpreter_db_add_extension (GFile *file,
|
|
GimpInterpreterDB *db,
|
|
gchar **tokens)
|
|
{
|
|
const gchar *name = tokens[0];
|
|
const gchar *extension = tokens[3];
|
|
const gchar *program = tokens[5];
|
|
|
|
if (! g_hash_table_lookup (db->extension_names, name))
|
|
{
|
|
gchar *prog;
|
|
|
|
if (extension[0] == '\0' || extension[0] == '/')
|
|
return FALSE;
|
|
|
|
if (! g_file_test (program, G_FILE_TEST_IS_EXECUTABLE))
|
|
{
|
|
prog = g_find_program_in_path (program);
|
|
if (! prog || ! g_file_test (prog, G_FILE_TEST_IS_EXECUTABLE))
|
|
{
|
|
g_message (_("Bad interpreter referenced in interpreter file %s: %s"),
|
|
gimp_file_get_utf8_name (file),
|
|
gimp_filename_to_utf8 (program));
|
|
if (prog)
|
|
g_free (prog);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
prog = g_strdup (program);
|
|
|
|
g_hash_table_insert (db->extensions, g_strdup (extension), prog);
|
|
g_hash_table_insert (db->extension_names, g_strdup (name), prog);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
scanarg (const 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 *s = from;
|
|
gchar *p = from;
|
|
gchar c;
|
|
|
|
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_slice_new (GimpInterpreterMagic);
|
|
|
|
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_slice_free (GimpInterpreterMagic, 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;
|
|
GHashTable *extensions;
|
|
|
|
for (list = db->magics; list; list = list->next)
|
|
{
|
|
GimpInterpreterMagic *magic = list->data;
|
|
const gchar *program;
|
|
|
|
program = g_hash_table_lookup (db->programs, magic->program);
|
|
|
|
if (program != NULL)
|
|
{
|
|
g_free (magic->program);
|
|
magic->program = g_strdup (program);
|
|
}
|
|
}
|
|
|
|
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;
|
|
gchar *p;
|
|
const gchar *program;
|
|
|
|
filename = g_path_get_basename (program_path);
|
|
|
|
p = strrchr (filename, '.');
|
|
if (! p)
|
|
{
|
|
g_free (filename);
|
|
return NULL;
|
|
}
|
|
|
|
program = g_hash_table_lookup (db->extensions, p + 1);
|
|
|
|
g_free (filename);
|
|
|
|
return g_strdup (program);
|
|
}
|
|
|
|
static gchar *
|
|
resolve_sh_bang (GimpInterpreterDB *db,
|
|
const gchar *program_path,
|
|
gchar *buffer,
|
|
gssize len,
|
|
gchar **interp_arg)
|
|
{
|
|
gchar *cp;
|
|
gchar *name;
|
|
gchar *program;
|
|
|
|
cp = strchr (buffer, '\n');
|
|
if (! cp)
|
|
cp = buffer + len - 1;
|
|
|
|
*cp = '\0';
|
|
|
|
while (cp > buffer)
|
|
{
|
|
cp--;
|
|
if ((*cp == ' ') || (*cp == '\t') || (*cp == '\r'))
|
|
*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)
|
|
{
|
|
/* Shift program name and arguments to the right, if and
|
|
* only if we recorded a specific interpreter for such
|
|
* script. Otherwise let `env` tool do its job.
|
|
*/
|
|
name = cp;
|
|
|
|
for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++)
|
|
;
|
|
|
|
while ((*cp == ' ') || (*cp == '\t'))
|
|
*cp++ = '\0';
|
|
}
|
|
}
|
|
|
|
if (*cp)
|
|
*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 NULL;
|
|
}
|
|
|
|
gchar *
|
|
gimp_interpreter_db_resolve (GimpInterpreterDB *db,
|
|
const gchar *program_path,
|
|
gchar **interp_arg)
|
|
{
|
|
GFile *file;
|
|
GInputStream *input;
|
|
gchar *program = NULL;
|
|
|
|
g_return_val_if_fail (GIMP_IS_INTERPRETER_DB (db), NULL);
|
|
g_return_val_if_fail (program_path != NULL, NULL);
|
|
g_return_val_if_fail (interp_arg != NULL, NULL);
|
|
|
|
*interp_arg = NULL;
|
|
|
|
file = g_file_new_for_path (program_path);
|
|
input = G_INPUT_STREAM (g_file_read (file, NULL, NULL));
|
|
g_object_unref (file);
|
|
|
|
if (input)
|
|
{
|
|
gsize bytes_read;
|
|
gchar buffer[BUFSIZE];
|
|
|
|
memset (buffer, 0, sizeof (buffer));
|
|
g_input_stream_read_all (input, buffer,
|
|
sizeof (buffer) - 1, /* leave one nul at the end */
|
|
&bytes_read, NULL, NULL);
|
|
g_object_unref (input);
|
|
|
|
if (bytes_read)
|
|
{
|
|
if (bytes_read > 3 && buffer[0] == '#' && buffer[1] == '!')
|
|
program = resolve_sh_bang (db, program_path, buffer, bytes_read, interp_arg);
|
|
|
|
if (! program)
|
|
program = resolve_magic (db, program_path, buffer);
|
|
}
|
|
}
|
|
|
|
if (! program)
|
|
program = resolve_extension (db, program_path);
|
|
|
|
return program;
|
|
}
|
|
|
|
static void
|
|
collect_extensions (const gchar *ext,
|
|
const gchar *program G_GNUC_UNUSED,
|
|
GString *str)
|
|
{
|
|
if (str->len)
|
|
g_string_append_c (str, G_SEARCHPATH_SEPARATOR);
|
|
|
|
g_string_append_c (str, '.');
|
|
g_string_append (str, ext);
|
|
}
|
|
|
|
/**
|
|
* gimp_interpreter_db_get_extensions:
|
|
* @db:
|
|
*
|
|
* Return value: a newly allocated string with all registered file
|
|
* extensions separated by %G_SEARCHPATH_SEPARATOR;
|
|
* or %NULL if no extensions are registered
|
|
**/
|
|
gchar *
|
|
gimp_interpreter_db_get_extensions (GimpInterpreterDB *db)
|
|
{
|
|
GString *str;
|
|
|
|
g_return_val_if_fail (GIMP_IS_INTERPRETER_DB (db), NULL);
|
|
|
|
if (g_hash_table_size (db->extensions) == 0)
|
|
return NULL;
|
|
|
|
str = g_string_new (NULL);
|
|
|
|
g_hash_table_foreach (db->extensions, (GHFunc) collect_extensions, str);
|
|
|
|
return g_string_free (str, FALSE);
|
|
}
|