880 lines
21 KiB
C
880 lines
21 KiB
C
|
/* PIKA - Photo and Image Kooker Application
|
||
|
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||
|
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||
|
*
|
||
|
* Original copyright, applying to most contents (license remains unchanged):
|
||
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||
|
*
|
||
|
* pikainterpreterdb.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 "libpikabase/pikabase.h"
|
||
|
|
||
|
#include "plug-in-types.h"
|
||
|
|
||
|
#include "pikainterpreterdb.h"
|
||
|
|
||
|
#include "pika-intl.h"
|
||
|
|
||
|
|
||
|
#define BUFSIZE 4096
|
||
|
|
||
|
|
||
|
typedef struct _PikaInterpreterMagic PikaInterpreterMagic;
|
||
|
|
||
|
struct _PikaInterpreterMagic
|
||
|
{
|
||
|
gulong offset;
|
||
|
gchar *magic;
|
||
|
gchar *mask;
|
||
|
guint size;
|
||
|
gchar *program;
|
||
|
};
|
||
|
|
||
|
|
||
|
static void pika_interpreter_db_finalize (GObject *object);
|
||
|
|
||
|
static void pika_interpreter_db_load_interp_file (PikaInterpreterDB *db,
|
||
|
GFile *file);
|
||
|
|
||
|
static void pika_interpreter_db_add_program (PikaInterpreterDB *db,
|
||
|
GFile *file,
|
||
|
gchar *buffer);
|
||
|
static void pika_interpreter_db_add_binfmt_misc (PikaInterpreterDB *db,
|
||
|
GFile *file,
|
||
|
gchar *buffer);
|
||
|
|
||
|
static gboolean pika_interpreter_db_add_extension (GFile *file,
|
||
|
PikaInterpreterDB *db,
|
||
|
gchar **tokens);
|
||
|
static gboolean pika_interpreter_db_add_magic (PikaInterpreterDB *db,
|
||
|
gchar **tokens);
|
||
|
|
||
|
static void pika_interpreter_db_clear_magics (PikaInterpreterDB *db);
|
||
|
|
||
|
static void pika_interpreter_db_resolve_programs (PikaInterpreterDB *db);
|
||
|
|
||
|
|
||
|
G_DEFINE_TYPE (PikaInterpreterDB, pika_interpreter_db, G_TYPE_OBJECT)
|
||
|
|
||
|
#define parent_class pika_interpreter_db_parent_class
|
||
|
|
||
|
|
||
|
static void
|
||
|
pika_interpreter_db_class_init (PikaInterpreterDBClass *class)
|
||
|
{
|
||
|
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||
|
|
||
|
object_class->finalize = pika_interpreter_db_finalize;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
pika_interpreter_db_init (PikaInterpreterDB *db)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
pika_interpreter_db_finalize (GObject *object)
|
||
|
{
|
||
|
PikaInterpreterDB *db = PIKA_INTERPRETER_DB (object);
|
||
|
|
||
|
pika_interpreter_db_clear (db);
|
||
|
|
||
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||
|
}
|
||
|
|
||
|
PikaInterpreterDB *
|
||
|
pika_interpreter_db_new (gboolean verbose)
|
||
|
{
|
||
|
PikaInterpreterDB *db = g_object_new (PIKA_TYPE_INTERPRETER_DB, NULL);
|
||
|
|
||
|
db->verbose = verbose;
|
||
|
|
||
|
return db;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
pika_interpreter_db_load (PikaInterpreterDB *db,
|
||
|
GList *path)
|
||
|
{
|
||
|
GList *list;
|
||
|
|
||
|
g_return_if_fail (PIKA_IS_INTERPRETER_DB (db));
|
||
|
|
||
|
pika_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);
|
||
|
|
||
|
pika_interpreter_db_load_interp_file (db, file);
|
||
|
|
||
|
g_object_unref (file);
|
||
|
}
|
||
|
|
||
|
g_object_unref (info);
|
||
|
}
|
||
|
|
||
|
g_object_unref (enumerator);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pika_interpreter_db_resolve_programs (db);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
pika_interpreter_db_clear (PikaInterpreterDB *db)
|
||
|
{
|
||
|
g_return_if_fail (PIKA_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;
|
||
|
}
|
||
|
|
||
|
pika_interpreter_db_clear_magics (db);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
pika_interpreter_db_load_interp_file (PikaInterpreterDB *db,
|
||
|
GFile *file)
|
||
|
{
|
||
|
GInputStream *input;
|
||
|
GDataInputStream *data_input;
|
||
|
gchar *buffer;
|
||
|
gsize buffer_len;
|
||
|
GError *error = NULL;
|
||
|
|
||
|
if (db->verbose)
|
||
|
g_print ("Parsing '%s'\n", pika_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"),
|
||
|
pika_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] == '/'))
|
||
|
{
|
||
|
pika_interpreter_db_add_program (db, file, buffer);
|
||
|
}
|
||
|
else if (! g_ascii_isspace (buffer[0]) && (buffer[0] != '\0'))
|
||
|
{
|
||
|
pika_interpreter_db_add_binfmt_misc (db, file, buffer);
|
||
|
}
|
||
|
|
||
|
g_free (buffer);
|
||
|
}
|
||
|
|
||
|
if (error)
|
||
|
{
|
||
|
g_message (_("Error reading '%s': %s"),
|
||
|
pika_file_get_utf8_name (file),
|
||
|
error->message);
|
||
|
g_clear_error (&error);
|
||
|
}
|
||
|
|
||
|
g_object_unref (data_input);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
pika_interpreter_db_add_program (PikaInterpreterDB *db,
|
||
|
GFile *file,
|
||
|
gchar *buffer)
|
||
|
{
|
||
|
gchar *name;
|
||
|
gchar *program;
|
||
|
gchar *p;
|
||
|
|
||
|
p = strchr (buffer, '=');
|
||
|
if (! p)
|
||
|
return;
|
||
|
|
||
|
*p = '\0';
|
||
|
|
||
|
name = buffer;
|
||
|
program = p + 1;
|
||
|
g_strchomp (program);
|
||
|
|
||
|
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"),
|
||
|
pika_file_get_utf8_name (file),
|
||
|
pika_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
|
||
|
pika_interpreter_db_add_binfmt_misc (PikaInterpreterDB *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 (! pika_interpreter_db_add_extension (file, db, tokens))
|
||
|
goto bail;
|
||
|
break;
|
||
|
|
||
|
case 'M':
|
||
|
if (! pika_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"),
|
||
|
pika_file_get_utf8_name (file));
|
||
|
|
||
|
out:
|
||
|
g_strfreev (tokens);
|
||
|
}
|
||
|
|
||
|
static gboolean
|
||
|
pika_interpreter_db_add_extension (GFile *file,
|
||
|
PikaInterpreterDB *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"),
|
||
|
pika_file_get_utf8_name (file),
|
||
|
pika_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
|
||
|
pika_interpreter_db_add_magic (PikaInterpreterDB *db,
|
||
|
gchar **tokens)
|
||
|
{
|
||
|
PikaInterpreterMagic *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 (PikaInterpreterMagic);
|
||
|
|
||
|
interp_magic->offset = offset;
|
||
|
interp_magic->magic = g_memdup2 (magic, size);
|
||
|
interp_magic->mask = g_memdup2 (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
|
||
|
pika_interpreter_db_clear_magics (PikaInterpreterDB *db)
|
||
|
{
|
||
|
PikaInterpreterMagic *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 (PikaInterpreterMagic, 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)
|
||
|
{
|
||
|
PikaInterpreterDB *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
|
||
|
pika_interpreter_db_resolve_programs (PikaInterpreterDB *db)
|
||
|
{
|
||
|
GSList *list;
|
||
|
GHashTable *extensions;
|
||
|
|
||
|
for (list = db->magics; list; list = list->next)
|
||
|
{
|
||
|
PikaInterpreterMagic *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)
|
||
|
{
|
||
|
PikaInterpreterMagic *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 (PikaInterpreterDB *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 (PikaInterpreterDB *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 (PikaInterpreterDB *db,
|
||
|
const gchar *program_path,
|
||
|
gchar *buffer)
|
||
|
{
|
||
|
GSList *list;
|
||
|
PikaInterpreterMagic *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 *
|
||
|
pika_interpreter_db_resolve (PikaInterpreterDB *db,
|
||
|
const gchar *program_path,
|
||
|
gchar **interp_arg)
|
||
|
{
|
||
|
GFile *file;
|
||
|
GInputStream *input;
|
||
|
gchar *program = NULL;
|
||
|
|
||
|
g_return_val_if_fail (PIKA_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);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* pika_interpreter_db_get_extensions:
|
||
|
* @db:
|
||
|
*
|
||
|
* Returns: a newly allocated string with all registered file
|
||
|
* extensions separated by %G_SEARCHPATH_SEPARATOR;
|
||
|
* or %NULL if no extensions are registered
|
||
|
**/
|
||
|
gchar *
|
||
|
pika_interpreter_db_get_extensions (PikaInterpreterDB *db)
|
||
|
{
|
||
|
GString *str;
|
||
|
|
||
|
g_return_val_if_fail (PIKA_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);
|
||
|
}
|