summaryrefslogtreecommitdiffstats
path: root/flow/gsl/gslmagic.c
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-01-05 00:01:18 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-01-05 00:01:18 +0000
commit42995d7bf396933ee60c5f89c354ea89cf13df0d (patch)
treecfdcea0ac57420e7baf570bfe435e107bb842541 /flow/gsl/gslmagic.c
downloadarts-42995d7bf396933ee60c5f89c354ea89cf13df0d.tar.gz
arts-42995d7bf396933ee60c5f89c354ea89cf13df0d.zip
Copy of aRts for Trinity modifications
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/dependencies/arts@1070145 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'flow/gsl/gslmagic.c')
-rw-r--r--flow/gsl/gslmagic.c711
1 files changed, 711 insertions, 0 deletions
diff --git a/flow/gsl/gslmagic.c b/flow/gsl/gslmagic.c
new file mode 100644
index 0000000..29486e3
--- /dev/null
+++ b/flow/gsl/gslmagic.c
@@ -0,0 +1,711 @@
+/* GSL - Generic Sound Layer
+ * Copyright (C) 2000-2002 Tim Janik
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library 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 Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#include "gslmagic.h"
+
+#include "gslcommon.h"
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+
+/* --- defines --- */
+#define BFILE_BSIZE (768) /* amount of buffering */
+#define MAX_MAGIC_STRING (256) /* must be < BFILE_BSIZE / 2 */
+
+
+/* --- typedefs & structures --- */
+typedef struct _Magic Magic;
+typedef struct _BFile BFile;
+struct _BFile
+{
+ gint fd;
+ guint file_size;
+ guint8 header[BFILE_BSIZE];
+ guint offset;
+ guint8 buffer[BFILE_BSIZE];
+};
+
+
+/* --- prototypes --- */
+static Magic* magic_create (gchar *magic_string,
+ const gchar *original);
+static gboolean magic_match_file (BFile *bfile,
+ Magic *magics);
+static gboolean bfile_open (BFile *bfile,
+ const gchar *file_name);
+static gboolean bfile_read (BFile *bfile,
+ guint offset,
+ void *mem,
+ guint n_bytes);
+static void bfile_close (BFile *bfile);
+static guint bfile_get_size (BFile *bfile);
+
+
+/* --- functions --- */
+GslMagic*
+gsl_magic_list_match_file (GslRing *magic_list,
+ const gchar *file_name)
+{
+ GslMagic *rmagic = NULL;
+ BFile bfile = { -1, };
+
+ g_return_val_if_fail (file_name != NULL, NULL);
+
+ if (bfile_open (&bfile, file_name))
+ {
+ gchar *extension = strrchr (file_name, '.');
+ gint rpriority = G_MAXINT;
+ GslRing *node;
+
+ /* we do a quick scan by extension first */
+ if (!rmagic && extension)
+ for (node = magic_list; node; node = gsl_ring_walk (magic_list, node))
+ {
+ GslMagic *magic = node->data;
+
+ if (!magic->extension
+ || strcmp (magic->extension, extension) != 0
+ || rpriority < magic->priority
+ || (rmagic && rpriority == magic->priority))
+ continue;
+ if (magic_match_file (&bfile, magic->match_list))
+ {
+ rmagic = magic;
+ rpriority = rmagic->priority;
+ }
+ }
+ /* then we do a normal walk but skip extension matches */
+ if (!rmagic && extension)
+ for (node = magic_list; node; node = gsl_ring_walk (magic_list, node))
+ {
+ GslMagic *magic = node->data;
+
+ if ((magic->extension && strcmp (magic->extension, extension) == 0)
+ || rpriority < magic->priority
+ || (rmagic && rpriority == magic->priority))
+ continue;
+ if (magic_match_file (&bfile, magic->match_list))
+ {
+ rmagic = magic;
+ rpriority = rmagic->priority;
+ }
+ }
+ /* for no extension, we do a full walk */
+ if (!rmagic && !extension)
+ for (node = magic_list; node; node = gsl_ring_walk (magic_list, node))
+ {
+ GslMagic *magic = node->data;
+
+ if (rpriority < magic->priority ||
+ (rmagic && rpriority == magic->priority))
+ continue;
+ if (magic_match_file (&bfile, magic->match_list))
+ {
+ rmagic = magic;
+ rpriority = rmagic->priority;
+ }
+ }
+ bfile_close (&bfile);
+ }
+
+ return rmagic;
+}
+
+GslMagic*
+gsl_magic_create (gpointer data,
+ gint priority,
+ const gchar *extension,
+ const gchar *magic_spec)
+{
+ GslMagic *magic;
+ Magic *match_list;
+ gchar *magic_string;
+
+ g_return_val_if_fail (magic_spec != NULL, NULL);
+
+ magic_string = g_strdup (magic_spec);
+ match_list = magic_create (magic_string, magic_spec);
+ g_free (magic_string);
+ if (!match_list)
+ return NULL;
+
+ magic = g_new (GslMagic, 1);
+ magic->data = data;
+ magic->extension = g_strdup (extension);
+ magic->priority = priority;
+ magic->match_list = match_list;
+
+ return magic;
+}
+
+
+/* --- Magic creation/checking --- */
+typedef enum
+{
+ MAGIC_CHECK_ANY,
+ MAGIC_CHECK_INT_EQUAL,
+ MAGIC_CHECK_INT_GREATER,
+ MAGIC_CHECK_INT_SMALLER,
+ MAGIC_CHECK_UINT_GREATER,
+ MAGIC_CHECK_UINT_SMALLER,
+ MAGIC_CHECK_UINT_ZEROS,
+ MAGIC_CHECK_UINT_ONES,
+ MAGIC_CHECK_STRING_EQUAL,
+ MAGIC_CHECK_STRING_GREATER,
+ MAGIC_CHECK_STRING_SMALLER
+} MagicCheckType;
+typedef union
+{
+ gint32 v_int32;
+ guint32 v_uint32;
+ gchar *v_string;
+} MagicData;
+struct _Magic
+{
+ Magic *next;
+ gulong offset;
+ guint data_size;
+ MagicCheckType magic_check;
+ guint32 data_mask;
+ MagicData value;
+ guint read_string : 1;
+ guint read_size : 1;
+ guint need_swap : 1;
+ guint cmp_unsigned : 1;
+};
+static const gchar *magic_field_delims = " \t,";
+static const gchar *magic_string_delims = " \t\n\r";
+
+static gboolean
+magic_parse_test (Magic *magic,
+ const gchar *string)
+{
+ if (!magic->read_string)
+ {
+ gchar *f = NULL;
+
+ if (string[0] == '<' || string[0] == '>')
+ {
+ if (magic->cmp_unsigned)
+ magic->magic_check = string[0] == '<' ? MAGIC_CHECK_UINT_SMALLER : MAGIC_CHECK_UINT_GREATER;
+ else
+ magic->magic_check = string[0] == '<' ? MAGIC_CHECK_INT_SMALLER : MAGIC_CHECK_INT_GREATER;
+ string += 1;
+ }
+ else if (string[0] == '^' || string[0] == '&')
+ {
+ magic->magic_check = string[0] == '&' ? MAGIC_CHECK_UINT_ONES : MAGIC_CHECK_UINT_ZEROS;
+ string += 1;
+ }
+ else if (string[0] == 'x')
+ {
+ magic->magic_check = MAGIC_CHECK_ANY;
+ string += 1;
+ }
+ else
+ {
+ string += string[0] == '=';
+ magic->magic_check = MAGIC_CHECK_INT_EQUAL;
+ }
+ if (string[0] == '0')
+ magic->value.v_int32 = strtol (string, &f, string[1] == 'x' ? 16 : 8);
+ else
+ magic->value.v_int32 = strtol (string, &f, 10);
+
+ return *string == 0 || !f || *f == 0;
+ }
+ else
+ {
+ gchar tmp_string[MAX_MAGIC_STRING + 1];
+ guint n = 0;
+
+ if (string[0] == '<' || string[0] == '>')
+ {
+ magic->magic_check = string[0] == '<' ? MAGIC_CHECK_STRING_SMALLER : MAGIC_CHECK_STRING_GREATER;
+ string += 1;
+ }
+ else
+ {
+ string += string[0] == '=';
+ magic->magic_check = MAGIC_CHECK_STRING_EQUAL;
+ }
+ while (n < MAX_MAGIC_STRING && string[n] && !strchr (magic_string_delims, string[n]))
+ {
+ if (string[n] != '\\')
+ tmp_string[n] = string[n];
+ else switch ((++string)[n])
+ {
+ case '\\': tmp_string[n] = '\\'; break;
+ case 't': tmp_string[n] = '\t'; break;
+ case 'n': tmp_string[n] = '\n'; break;
+ case 'r': tmp_string[n] = '\r'; break;
+ case 'b': tmp_string[n] = '\b'; break;
+ case 'f': tmp_string[n] = '\f'; break;
+ case 's': tmp_string[n] = ' '; break;
+ case 'e': tmp_string[n] = 27; break;
+ default:
+ if (string[n] >= '0' && string[n] <= '7')
+ {
+ tmp_string[n] = string[n] - '0';
+ if (string[n + 1] >= '0' && string[n + 1] <= '7')
+ {
+ string += 1;
+ tmp_string[n] *= 8;
+ tmp_string[n] += string[n] - '0';
+ if (string[n + 1] >= '0' && string[n + 1] <= '7')
+ {
+ string += 1;
+ tmp_string[n] *= 8;
+ tmp_string[n] += string[n] - '0';
+ }
+ }
+ }
+ else
+ tmp_string[n] = string[n];
+ break;
+ }
+ n++;
+ }
+ tmp_string[n] = 0;
+ magic->data_size = n;
+ magic->value.v_string = g_strdup (tmp_string);
+
+ return TRUE;
+ }
+}
+
+static gboolean
+magic_parse_type (Magic *magic,
+ const gchar *string)
+{
+ gchar *f = NULL;
+
+ if (string[0] == 'u')
+ {
+ string += 1;
+ magic->cmp_unsigned = TRUE;
+ }
+ if (strncmp (string, "byte", 4) == 0)
+ {
+ string += 4;
+ magic->data_size = 1;
+ }
+ else if (strncmp (string, "short", 5) == 0)
+ {
+ string += 5;
+ magic->data_size = 2;
+ }
+ else if (strncmp (string, "leshort", 7) == 0)
+ {
+ string += 7;
+ magic->data_size = 2;
+ magic->need_swap = G_BYTE_ORDER != G_LITTLE_ENDIAN;
+ }
+ else if (strncmp (string, "beshort", 7) == 0)
+ {
+ string += 7;
+ magic->data_size = 2;
+ magic->need_swap = G_BYTE_ORDER != G_BIG_ENDIAN;
+ }
+ else if (strncmp (string, "long", 4) == 0)
+ {
+ string += 4;
+ magic->data_size = 4;
+ }
+ else if (strncmp (string, "lelong", 6) == 0)
+ {
+ string += 6;
+ magic->data_size = 4;
+ magic->need_swap = G_BYTE_ORDER != G_LITTLE_ENDIAN;
+ }
+ else if (strncmp (string, "belong", 6) == 0)
+ {
+ string += 6;
+ magic->data_size = 4;
+ magic->need_swap = G_BYTE_ORDER != G_BIG_ENDIAN;
+ }
+#if 0
+ else if (strncmp (string, "size", 4) == 0)
+ {
+ string += 4;
+ magic->data_size = 4;
+ magic->read_size = TRUE;
+ magic->cmp_unsigned = TRUE;
+ }
+#endif
+ else if (strncmp (string, "string", 6) == 0)
+ {
+ string += 6;
+ magic->data_size = 0;
+ magic->read_string = TRUE;
+ }
+ if (string[0] == '&')
+ {
+ string += 1;
+ if (string[0] == '0')
+ magic->data_mask = strtol (string, &f, string[1] == 'x' ? 16 : 8);
+ else
+ magic->data_mask = strtol (string, &f, 10);
+ if (f && *f != 0)
+ return FALSE;
+ while (*string)
+ string++;
+ }
+ else
+ magic->data_mask = 0xffffffff;
+
+ return *string == 0;
+}
+
+static gboolean
+magic_parse_offset (Magic *magic,
+ gchar *string)
+{
+ gchar *f = NULL;
+
+ if (string[0] == '0')
+ magic->offset = strtol (string, &f, string[1] == 'x' ? 16 : 8);
+ else
+ magic->offset = strtol (string, &f, 10);
+
+ return !f || *f == 0;
+}
+
+static Magic*
+magic_create (gchar *magic_string,
+ const gchar *original)
+#define SKIP_CLEAN(s) { while (*s && !strchr(magic_field_delims, *s)) s++; \
+ while (*s && strchr(magic_field_delims, *s)) \
+ *(s++)=0;}
+{
+ Magic *magics = NULL;
+ gchar *p = magic_string;
+
+ while (p && *p)
+ {
+ gchar *next_line;
+
+ if (*p == '#' || *p == '\n')
+ {
+ next_line = strchr (p, '\n');
+ if (next_line)
+ next_line++;
+ }
+ else
+ {
+ Magic *tmp = magics;
+
+ magics = g_new0 (Magic, 1);
+ magics->next = tmp;
+
+ magic_string = p;
+ SKIP_CLEAN (p);
+ if (!magic_parse_offset (magics, magic_string))
+ {
+ g_warning ("unable to parse magic offset \"%s\" from \"%s\"", magic_string, original);
+ return NULL;
+ }
+ magic_string = p;
+ SKIP_CLEAN (p);
+ if (!magic_parse_type (magics, magic_string))
+ {
+ g_warning ("unable to parse magic type \"%s\" from \"%s\"", magic_string, original);
+ return NULL;
+ }
+ magic_string = p;
+ next_line = strchr (magic_string, '\n');
+ if (next_line)
+ *(next_line++) = 0;
+ if (!magics->read_string)
+ SKIP_CLEAN (p);
+ if (!magic_parse_test (magics, magic_string))
+ {
+ g_warning ("unable to parse magic test \"%s\" from \"%s\"", magic_string, original);
+ return NULL;
+ }
+ }
+ p = next_line;
+ }
+
+ return magics;
+}
+
+static gboolean
+magic_check_data (Magic *magic,
+ MagicData *data)
+{
+ gint cmp = 0;
+
+ switch (magic->magic_check)
+ {
+ guint l;
+ case MAGIC_CHECK_ANY:
+ cmp = 1;
+ break;
+ case MAGIC_CHECK_INT_EQUAL:
+ data->v_int32 &= magic->data_mask;
+ cmp = data->v_int32 == magic->value.v_int32;
+ break;
+ case MAGIC_CHECK_INT_GREATER:
+ data->v_int32 &= magic->data_mask;
+ cmp = data->v_int32 > magic->value.v_int32;
+ break;
+ case MAGIC_CHECK_INT_SMALLER:
+ data->v_int32 &= magic->data_mask;
+ cmp = data->v_int32 < magic->value.v_int32;
+ break;
+ case MAGIC_CHECK_UINT_GREATER:
+ data->v_uint32 &= magic->data_mask;
+ cmp = data->v_uint32 > magic->value.v_uint32;
+ break;
+ case MAGIC_CHECK_UINT_SMALLER:
+ data->v_uint32 &= magic->data_mask;
+ cmp = data->v_uint32 < magic->value.v_uint32;
+ break;
+ case MAGIC_CHECK_UINT_ZEROS:
+ data->v_uint32 &= magic->data_mask;
+ cmp = (data->v_int32 & magic->value.v_int32) == 0;
+ break;
+ case MAGIC_CHECK_UINT_ONES:
+ data->v_uint32 &= magic->data_mask;
+ cmp = (data->v_int32 & magic->value.v_int32) == magic->value.v_int32;
+ break;
+ case MAGIC_CHECK_STRING_EQUAL:
+ l = magic->data_size < 1 ? strlen (data->v_string) : magic->data_size;
+ cmp = strncmp (data->v_string, magic->value.v_string, l) == 0;
+ break;
+ case MAGIC_CHECK_STRING_GREATER:
+ l = magic->data_size < 1 ? strlen (data->v_string) : magic->data_size;
+ cmp = strncmp (data->v_string, magic->value.v_string, l) > 0;
+ break;
+ case MAGIC_CHECK_STRING_SMALLER:
+ l = magic->data_size < 1 ? strlen (data->v_string) : magic->data_size;
+ cmp = strncmp (data->v_string, magic->value.v_string, l) < 0;
+ break;
+ }
+
+ return cmp > 0;
+}
+
+static inline gboolean
+magic_read_data (BFile *bfile,
+ Magic *magic,
+ MagicData *data)
+{
+ guint file_size = bfile_get_size (bfile);
+
+ if (magic->read_size)
+ data->v_uint32 = file_size;
+ else if (magic->read_string)
+ {
+ guint l = magic->data_size;
+
+ if (l < 1 || l > MAX_MAGIC_STRING)
+ l = MIN (MAX_MAGIC_STRING, file_size - magic->offset);
+ if (!bfile_read (bfile, magic->offset, data->v_string, l))
+ return FALSE;
+ data->v_string[MAX (l, 0)] = 0;
+ }
+ else
+ {
+ if (magic->data_size == 4)
+ {
+ guint32 uint32 = 0;
+
+ if (!bfile_read (bfile, magic->offset, &uint32, 4))
+ return FALSE;
+ if (magic->need_swap)
+ data->v_uint32 = GUINT32_SWAP_LE_BE (uint32);
+ else
+ data->v_uint32 = uint32;
+ }
+ else if (magic->data_size == 2)
+ {
+ guint16 uint16 = 0;
+
+ if (!bfile_read (bfile, magic->offset, &uint16, 2))
+ return FALSE;
+ if (magic->need_swap)
+ uint16 = GUINT16_SWAP_LE_BE (uint16);
+ if (magic->cmp_unsigned)
+ data->v_uint32 = uint16;
+ else
+ data->v_int32 = (signed) uint16;
+ }
+ else if (magic->data_size == 1)
+ {
+ guint8 uint8;
+
+ if (!bfile_read (bfile, magic->offset, &uint8, 1))
+ return FALSE;
+ if (magic->cmp_unsigned)
+ data->v_uint32 = uint8;
+ else
+ data->v_int32 = (signed) uint8;
+ }
+ else
+ g_assert_not_reached ();
+ }
+
+ return TRUE;
+}
+
+static gboolean
+magic_match_file (BFile *bfile,
+ Magic *magics)
+{
+ g_return_val_if_fail (bfile != NULL, FALSE);
+ g_return_val_if_fail (magics != NULL, FALSE);
+
+ do
+ {
+ gchar data_string[MAX_MAGIC_STRING + 1];
+ MagicData data;
+
+ if (magics->read_string)
+ data.v_string = data_string;
+ else
+ data.v_uint32 = 0;
+
+ if (!magic_read_data (bfile, magics, &data) ||
+ !magic_check_data (magics, &data))
+ return FALSE;
+ magics = magics->next;
+ }
+ while (magics);
+
+ return TRUE;
+}
+
+
+/* --- buffered file, optimized for magic checks --- */
+static gboolean
+bfile_open (BFile *bfile,
+ const gchar *file_name)
+{
+ struct stat buf = { 0, };
+ gint ret;
+
+ g_return_val_if_fail (bfile != NULL, FALSE);
+ g_return_val_if_fail (bfile->fd < 0, FALSE);
+ g_return_val_if_fail (file_name != NULL, FALSE);
+
+ bfile->fd = open (file_name, O_RDONLY);
+ if (bfile->fd < 0)
+ return FALSE;
+
+ do
+ ret = fstat (bfile->fd, &buf);
+ while (ret < 0 && errno == EINTR);
+ if (ret < 0)
+ {
+ bfile_close (bfile);
+ return FALSE;
+ }
+ bfile->file_size = buf.st_size;
+
+ do
+ ret = read (bfile->fd, bfile->header, BFILE_BSIZE);
+ while (ret < 0 && errno == EINTR);
+ if (ret < 0)
+ {
+ bfile_close (bfile);
+ return FALSE;
+ }
+
+ bfile->offset = 0;
+ memcpy (bfile->buffer, bfile->header, BFILE_BSIZE);
+
+ return TRUE;
+}
+
+static gboolean
+bfile_read (BFile *bfile,
+ guint offset,
+ void *mem,
+ guint n_bytes)
+{
+ guint end = offset + n_bytes;
+ gint ret;
+
+ g_return_val_if_fail (bfile != NULL, FALSE);
+ g_return_val_if_fail (n_bytes < BFILE_BSIZE / 2, FALSE);
+
+ if (end > bfile->file_size || bfile->fd < 0)
+ return FALSE;
+
+ if (end < BFILE_BSIZE)
+ {
+ memcpy (mem, bfile->header + offset, n_bytes);
+ return TRUE;
+ }
+ if (offset >= bfile->offset && end < bfile->offset + BFILE_BSIZE)
+ {
+ memcpy (mem, bfile->buffer + offset - bfile->offset, n_bytes);
+ return TRUE;
+ }
+
+ bfile->offset = offset - BFILE_BSIZE / 8;
+ do
+ ret = lseek (bfile->fd, bfile->offset, SEEK_SET);
+ while (ret < 0 && errno == EINTR);
+ if (ret < 0)
+ {
+ bfile_close (bfile);
+ return FALSE;
+ }
+ do
+ ret = read (bfile->fd, bfile->buffer, BFILE_BSIZE);
+ while (ret < 0 && errno == EINTR);
+ if (ret < 0)
+ {
+ bfile_close (bfile);
+ return FALSE;
+ }
+ if (offset >= bfile->offset && end < bfile->offset + BFILE_BSIZE)
+ {
+ memcpy (mem, bfile->buffer + offset - bfile->offset, n_bytes);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static guint
+bfile_get_size (BFile *bfile)
+{
+ g_return_val_if_fail (bfile != NULL, 0);
+
+ return bfile->fd >= 0 ? bfile->file_size : 0;
+}
+
+static void
+bfile_close (BFile *bfile)
+{
+ g_return_if_fail (bfile != NULL);
+
+ if (bfile->fd >= 0)
+ close (bfile->fd);
+ bfile->fd = -1;
+}