/*
* Copyright (C) 2005 Christophe Fergeau
*
*
* The code contained in this file is free software; you can redistribute
* it and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either version
* 2.1 of the License, or (at your option) any later version.
*
* This file 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this code; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* iTunes and iPod are trademarks of Apple
*
* This product is not supported/written/published by Apple!
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <glib.h>
#include <glib/gstdio.h>
#include "db-parse-context.h"
#include "db-itunes-parser.h"
#include "itdb_endianness.h"
#ifndef HAVE_G_MAPPED_FILE_UNREF
#define g_mapped_file_unref(f) g_mapped_file_free(f)
#endif
DBParseContext *
db_parse_context_new (const unsigned char *buffer, off_t len, guint byte_order)
{
DBParseContext *result;
result = g_new0 (DBParseContext, 1);
if (result == NULL) {
return NULL;
}
result->buffer = buffer;
result->cur_pos = buffer;
result->total_len = len;
result->byte_order = byte_order;
return result;
}
void
db_parse_context_destroy (DBParseContext *ctx)
{
g_return_if_fail (ctx != NULL);
if (ctx->mapped_file) {
g_mapped_file_unref(ctx->mapped_file);
}
g_free (ctx);
}
static void
db_parse_context_set_header_len (DBParseContext *ctx, off_t len)
{
/* FIXME: this can probably happen in malformed itunesdb files,
* don't g_assert on this, only output a warning
*/
g_assert ((ctx->cur_pos - ctx->buffer) <= len);
g_assert (len <= ctx->total_len);
ctx->header_len = len;
}
void
db_parse_context_set_total_len (DBParseContext *ctx, off_t len)
{
/* FIXME: this can probably happen in malformed itunesdb files,
* don't g_assert on this, only output a warning
*/
g_assert ((ctx->cur_pos - ctx->buffer) <= len);
if (ctx->header_len != 0) {
g_assert (len >= ctx->header_len);
}
ctx->total_len = len;
}
off_t
db_parse_context_get_remaining_length (DBParseContext *ctx)
{
if (ctx->header_len != 0) {
return ctx->header_len - (ctx->cur_pos - ctx->buffer);
} else {
return ctx->total_len - (ctx->cur_pos - ctx->buffer);
}
}
DBParseContext *
db_parse_context_get_sub_context (DBParseContext *ctx, off_t offset)
{
DBParseContext *sub_ctx;
if (offset >= ctx->total_len) {
return NULL;
}
sub_ctx = db_parse_context_new (&ctx->buffer[offset],
ctx->total_len - offset,
ctx->byte_order);
sub_ctx->db = ctx->db;
sub_ctx->artwork = ctx->artwork;
return sub_ctx;
}
DBParseContext *
db_parse_context_get_next_child (DBParseContext *ctx)
{
if (ctx->header_len == 0) {
return NULL;
}
if (ctx->header_len >= ctx->total_len) {
return NULL;
}
return db_parse_context_get_sub_context (ctx, ctx->header_len);
}
void *
db_parse_context_get_m_header_internal (DBParseContext *ctx, const char *id, off_t size)
{
MHeader *h;
char *header_id;
if (db_parse_context_get_remaining_length (ctx) < 8) {
return NULL;
}
h = (MHeader *)ctx->cur_pos;
header_id = g_strndup ((char *)h->header_id, 4);
if (ctx->byte_order == G_BIG_ENDIAN) {
g_strreverse (header_id);
}
if (strncmp (id, header_id, 4) != 0) {
g_free (header_id);
return NULL;
}
g_free (header_id);
/* FIXME: this test sucks for compat: if a field is smaller than
* expected, we probably should create a buffer of the appropriate
* size inited to 0, copy the data that is available in it and use
* that buffer in the rest of the code (maybe it's harmful to have
* some fields at 0 in some headers though...)
*/
if (get_gint32 (h->header_len, ctx->byte_order) < size) {
return NULL;
}
db_parse_context_set_header_len (ctx, get_gint32 (h->header_len,
ctx->byte_order));
return h;
}
DBParseContext *
db_parse_context_new_from_file (const char *filename, Itdb_DB *db)
{
DBParseContext *ctx;
Itdb_Device *device;
GError* error;
GMappedFile* mapped_file;
struct stat stat_buf;
ctx = NULL;
error = NULL;
mapped_file = NULL;
device = db_get_device (db);
g_return_val_if_fail (device, NULL);
if (g_stat (filename, &stat_buf) != 0) {
return NULL;
};
if (stat_buf.st_size > 64 * 1024 * 1024) {
g_warning ("%s is too big to be mmapped (%llu bytes)\n",
filename, (unsigned long long)stat_buf.st_size);
return NULL;
}
mapped_file = g_mapped_file_new(filename, FALSE, &error);
if (mapped_file == NULL) {
g_print ("Error while mapping %s: %s\n", filename,
error->message);
g_error_free(error);
return NULL;
}
if (device->byte_order == 0)
itdb_device_autodetect_endianess (device);
ctx = db_parse_context_new ((guchar *)g_mapped_file_get_contents(mapped_file),
g_mapped_file_get_length(mapped_file),
device->byte_order);
if (ctx == NULL) {
g_mapped_file_unref(mapped_file);
return NULL;
}
ctx->db = db;
ctx->mapped_file = mapped_file;
return ctx;
}