/* GLIB - Library of useful routines for C programming * gmappedfile.c: Simplified wrapper around the mmap() function. * * Copyright 2005 Matthias Clasen * * This library 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 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser 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. */ /* * Taken from glib-2.8 for glib < 2.8. Added noop P_() and _(). * Modified includes to make it compile outside of glib tarball. * Added empty file handling. */ #include "config.h" #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_MMAP #include #endif #include #if GLIB_CHECK_VERSION(2,6,0) #include #else #include "mooutils/mooutils-fs.h" #define g_open m_open #endif #include "mooutils/moocompat.h" #define P_(s) (s) #define _(s) (s) #ifdef G_OS_WIN32 #include #include #endif #ifndef _O_BINARY #define _O_BINARY 0 #endif #ifndef MAP_FAILED #define MAP_FAILED ((void *) -1) #endif struct _GMappedFile { gsize length; gchar *contents; #ifdef G_OS_WIN32 HANDLE mapping; #endif }; /** * g_mapped_file_new: * @filename: The path of the file to load, in the GLib filename encoding * @writable: wether the mapping should be writable * @error: return location for a #GError, or %NULL * * Maps a file into memory. On UNIX, this is using the mmap() function. * * If @writable is %TRUE, the mapped buffer may be modified, otherwise * it is an error to modify the mapped buffer. Modifications to the buffer * are not visible to other processes mapping the same file, and are not * written back to the file. * * Note that modifications of the underlying file might affect the contents * of the #GMappedFile. Therefore, mapping should only be used if the file * will not be modified, or if all modifications of the file are done * atomically (e.g. using g_file_set_contents()). * * Return value: a newly allocated #GMappedFile which must be freed * with g_mapped_file_free(), or %NULL if the mapping failed. * * Since: 2.8 */ GMappedFile * g_mapped_file_new (const gchar *filename, gboolean writable, GError **error) { GMappedFile *file; int fd; struct stat st; g_return_val_if_fail (filename != NULL, NULL); g_return_val_if_fail (!error || *error == NULL, NULL); fd = g_open (filename, (writable ? O_RDWR : O_RDONLY) | _O_BINARY, 0); if (fd == -1) { int save_errno = errno; gchar *display_filename = g_filename_display_name (filename); g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (save_errno), _("Failed to open file '%s': open() failed: %s"), display_filename, g_strerror (save_errno)); g_free (display_filename); return NULL; } file = g_new0 (GMappedFile, 1); if (fstat (fd, &st) == -1) { int save_errno = errno; gchar *display_filename = g_filename_display_name (filename); g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (save_errno), _("Failed to get attributes of file '%s': fstat() failed: %s"), display_filename, g_strerror (save_errno)); g_free (display_filename); goto out; } if (st.st_size == 0) { static char zero = 0; file->contents = &zero; file->length = 0; close (fd); return file; } file->contents = MAP_FAILED; #ifdef HAVE_MMAP if ((gsize) st.st_size > G_MAXSIZE) { errno = EINVAL; } else { file->length = (gsize) st.st_size; file->contents = (gchar *) mmap (NULL, file->length, writable ? PROT_READ|PROT_WRITE : PROT_READ, MAP_PRIVATE, fd, 0); } #endif #ifdef G_OS_WIN32 file->length = st.st_size; file->mapping = CreateFileMapping ((HANDLE) _get_osfhandle (fd), NULL, writable ? PAGE_WRITECOPY : PAGE_READONLY, 0, 0, NULL); if (file->mapping != NULL) { file->contents = MapViewOfFile (file->mapping, writable ? FILE_MAP_COPY : FILE_MAP_READ, 0, 0, 0); if (file->contents == NULL) { file->contents = MAP_FAILED; CloseHandle (file->mapping); file->mapping = NULL; } } #endif if (file->contents == MAP_FAILED) { int save_errno = errno; gchar *display_filename = g_filename_display_name (filename); g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (save_errno), _("Failed to map file '%s': mmap() failed: %s"), display_filename, g_strerror (save_errno)); g_free (display_filename); goto out; } close (fd); return file; out: close (fd); g_free (file); return NULL; } /** * g_mapped_file_get_length: * @file: a #GMappedFile * * Returns the length of the contents of a #GMappedFile. * * Returns: the length of the contents of @file. * * Since: 2.8 */ gsize g_mapped_file_get_length (GMappedFile *file) { g_return_val_if_fail (file != NULL, 0); return file->length; } /** * g_mapped_file_get_contents: * @file: a #GMappedFile * * Returns the contents of a #GMappedFile. * * Note that the contents may not be zero-terminated, * even if the #GMappedFile is backed by a text file. * * Returns: the contents of @file. * * Since: 2.8 */ gchar * g_mapped_file_get_contents (GMappedFile *file) { g_return_val_if_fail (file != NULL, NULL); return file->contents; } /** * g_mapped_file_free: * @file: a #GMappedFile * * Unmaps the buffer of @file and frees it. * * Since: 2.8 */ void g_mapped_file_free (GMappedFile *file) { g_return_if_fail (file != NULL); if (file->length) { #ifdef HAVE_MMAP munmap (file->contents, file->length); #endif #ifdef G_OS_WIN32 UnmapViewOfFile (file->contents); CloseHandle (file->mapping); #endif } g_free (file); }