gnucash/lib/libgsf-1.12.3/gsf/gsf-input-memory.c
Joshua Sled 94e9fe5e6f Fold branches/goffice-update/ back into trunk/.
git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@12096 57a11ea4-9604-0410-9ed3-97b8803252fd
2005-12-04 21:27:17 +00:00

314 lines
7.6 KiB
C

/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* gsf-input-memory.c:
*
* Copyright (C) 2002-2004 Jody Goldberg (jody@gnome.org)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2.1 of the GNU Lesser General Public
* License as published by the Free Software Foundation.
*
* 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 Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#include <gsf-config.h>
#include <string.h>
#include <gsf/gsf-input-memory.h>
#include <gsf/gsf-input-impl.h>
#include <gsf/gsf-impl-utils.h>
#include <gsf/gsf-utils.h>
#include <gsf/gsf-shared-memory.h>
#ifdef HAVE_GLIB26
#include <glib/gstdio.h>
#else
#include "glib24_26-compat.h"
#endif // HAVE_GLIB26
#ifdef HAVE_MMAP
#if defined(FREEBSD) || defined(__FreeBSD__)
/* We must keep the file open while pages are mapped. */
/* http://www.freebsd.org/cgi/query-pr.cgi?pr=48291 */
#define HAVE_BROKEN_MMAP
#endif /* defined(FREEBSD) || defined(__FreeBSD__) */
#elif defined(G_OS_WIN32)
#include <windows.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <io.h>
#include <fcntl.h>
#define MAP_FAILED NULL
#endif /* HAVE_MMAP */
#ifndef O_BINARY
#define O_BINARY 0
#endif
static GObjectClass *parent_class;
struct _GsfInputMemory {
GsfInput parent;
GsfSharedMemory *shared;
#ifdef HAVE_BROKEN_MMAP
int fd;
#endif
};
typedef GsfInputClass GsfInputMemoryClass;
/**
* gsf_input_memory_new:
* @buf: The input bytes
* @length: The length of @buf
* @needs_free: Whether you want this memory to be free'd at object destruction
*
* Returns: A new #GsfInputMemory
*/
GsfInput *
gsf_input_memory_new (guint8 const *buf, gsf_off_t length, gboolean needs_free)
{
GsfInputMemory *mem = g_object_new (GSF_INPUT_MEMORY_TYPE, NULL);
mem->shared = gsf_shared_memory_new ((void *)buf, length, needs_free);
gsf_input_set_size (GSF_INPUT (mem), length);
return GSF_INPUT (mem);
}
/**
* gsf_input_memory_new_clone:
* @buf: The input bytes
* @length: The length of @buf
*
* Returns: A new #GsfInputMemory
*/
GsfInput *
gsf_input_memory_new_clone (guint8 const *buf, gsf_off_t length)
{
GsfInputMemory *mem = NULL;
guint8 * cpy = g_try_malloc (length * sizeof (guint8));
if (cpy == NULL)
return NULL;
memcpy (cpy, buf, length);
mem = g_object_new (GSF_INPUT_MEMORY_TYPE, NULL);
mem->shared = gsf_shared_memory_new ((void *)cpy, length, TRUE);
gsf_input_set_size (GSF_INPUT (mem), length);
return GSF_INPUT (mem);
}
static void
gsf_input_memory_finalize (GObject *obj)
{
GsfInputMemory *mem = (GsfInputMemory *) (obj);
if (mem->shared)
g_object_unref (G_OBJECT (mem->shared));
#ifdef HAVE_BROKEN_MMAP
if (mem->fd != -1)
close (mem->fd);
#endif
parent_class->finalize (obj);
}
static GsfInput *
gsf_input_memory_dup (GsfInput *src_input, G_GNUC_UNUSED GError **err)
{
GsfInputMemory const *src = (GsfInputMemory *) (src_input);
GsfInputMemory *dst = g_object_new (GSF_INPUT_MEMORY_TYPE, NULL);
dst->shared = src->shared;
g_object_ref (G_OBJECT (dst->shared));
gsf_input_set_size (GSF_INPUT (dst), src->shared->size);
#ifdef HAVE_BROKEN_MMAP
if (src->fd != -1)
dst->fd = dup (src->fd);
#endif
return GSF_INPUT (dst);
}
static guint8 const *
gsf_input_memory_read (GsfInput *input, size_t num_bytes, guint8 *optional_buffer)
{
GsfInputMemory *mem = (GsfInputMemory *) (input);
guchar const *src = mem->shared->buf;
if (src == NULL)
return NULL;
if (optional_buffer) {
memcpy (optional_buffer, src + input->cur_offset, num_bytes);
return optional_buffer;
} else
return src + input->cur_offset;
}
static gboolean
gsf_input_memory_seek (G_GNUC_UNUSED GsfInput *input,
G_GNUC_UNUSED gsf_off_t offset,
G_GNUC_UNUSED GSeekType whence)
{
return FALSE;
}
static void
gsf_input_memory_init (GObject *obj)
{
GsfInputMemory *mem = (GsfInputMemory *) (obj);
mem->shared = NULL;
#ifdef HAVE_BROKEN_MMAP
mem->fd = -1;
#endif
}
static void
gsf_input_memory_class_init (GObjectClass *gobject_class)
{
GsfInputClass *input_class = GSF_INPUT_CLASS (gobject_class);
gobject_class->finalize = gsf_input_memory_finalize;
input_class->Dup = gsf_input_memory_dup;
input_class->Read = gsf_input_memory_read;
input_class->Seek = gsf_input_memory_seek;
parent_class = g_type_class_peek_parent (gobject_class);
}
GSF_CLASS (GsfInputMemory, gsf_input_memory,
gsf_input_memory_class_init, gsf_input_memory_init,
GSF_INPUT_TYPE)
/***************************************************************************/
#ifdef HAVE_MMAP
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#ifndef PROT_READ
#define PROT_READ 0x1
#endif /* PROT_READ */
#if !defined(MAP_FAILED) || defined(__osf__)
/* Someone needs their head examined - BSD ? */
# define MAP_FAILED ((void *)-1)
#endif /* !defined(MAP_FAILED) || defined(__osf__) */
#endif /* HAVE_MMAP */
/**
* gsf_input_mmap_new:
* @filename: The file on disk that you want to mmap
* @err: A #GError, or optionally %null
*
* Returns: A new #GsfInputMemory
*/
GsfInput *
gsf_input_mmap_new (char const *filename, GError **err)
{
#if defined(HAVE_MMAP) || defined(G_OS_WIN32)
GsfInputMemory *mem;
guint8 *buf = NULL;
struct stat st;
int fd;
size_t size;
fd = g_open (filename, O_RDONLY | O_BINARY, 0);
if (fd < 0 || fstat (fd, &st) < 0) {
if (err != NULL) {
int save_errno = errno;
char *utf8name = g_filename_display_name (filename);
*err = g_error_new (gsf_input_error_id (), 0,
"%s: %s",
utf8name, g_strerror (save_errno));
g_free (utf8name);
}
if (fd >= 0) close (fd);
return NULL;
}
if (!S_ISREG (st.st_mode)) {
if (err != NULL) {
char *utf8name = g_filename_display_name (filename);
*err = g_error_new (gsf_input_error_id (), 0,
"%s: Is not a regular file",
utf8name);
g_free (utf8name);
}
close (fd);
return NULL;
}
size = (size_t) st.st_size;
if ((off_t) size != st.st_size) { /* Check for overflow */
if (err != NULL) {
char *utf8name = g_filename_display_name (filename);
*err = g_error_new (gsf_input_error_id (), 0,
"%s: File too large to be memory mapped",
utf8name);
g_free (utf8name);
}
close (fd);
return NULL;
}
#ifdef G_OS_WIN32
{
HANDLE handle = CreateFileMapping ((HANDLE)_get_osfhandle (fd), NULL, PAGE_READONLY, 0, 0, NULL);
buf = MapViewOfFile (handle, FILE_MAP_READ, 0, 0, 0);
CloseHandle (handle);
}
#else
buf = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, (off_t) 0);
#endif
if (buf == MAP_FAILED) {
if (err != NULL) {
int save_errno = errno;
char *utf8name = g_filename_display_name (filename);
*err = g_error_new (gsf_input_error_id (), 0,
"%s: %s",
utf8name, g_strerror (save_errno));
g_free (utf8name);
}
close (fd);
return NULL;
}
mem = g_object_new (GSF_INPUT_MEMORY_TYPE, NULL);
mem->shared = gsf_shared_memory_mmapped_new (buf, (gsf_off_t) size);
gsf_input_set_size (GSF_INPUT (mem), (gsf_off_t) size);
gsf_input_set_name (GSF_INPUT (mem), filename);
#ifdef HAVE_BROKEN_MMAP
mem->fd = fd;
#else
close (fd);
#endif
return GSF_INPUT (mem);
#else
(void)filename;
if (err != NULL)
*err = g_error_new (gsf_input_error_id (), 0,
"mmap not supported");
return NULL;
#endif
}