diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-05 00:01:18 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-05 00:01:18 +0000 |
commit | 42995d7bf396933ee60c5f89c354ea89cf13df0d (patch) | |
tree | cfdcea0ac57420e7baf570bfe435e107bb842541 /flow/gsl/gslmagic.c | |
download | arts-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.c | 711 |
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; +} |