mirror of
				https://gitlab.gnome.org/GNOME/glib.git
				synced 2025-10-31 16:32:18 +01:00 
			
		
		
		
	Since the previous commit, the generic `GInputStream` implementation of `skip()` is now equivalent, and results in the same calls to `lseek()`. Heavily based on an approach by Dan Winship in https://bugzilla.gnome.org/show_bug.cgi?id=681374. Signed-off-by: Philip Withnall <pwithnall@endlessos.org> Helps: #587
		
			
				
	
	
		
			306 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			306 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* GIO - GLib Input, Output and Streaming Library
 | |
|  * 
 | |
|  * Copyright (C) 2006-2007 Red Hat, Inc.
 | |
|  *
 | |
|  * 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.1 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, see <http://www.gnu.org/licenses/>.
 | |
|  *
 | |
|  * Author: Alexander Larsson <alexl@redhat.com>
 | |
|  */
 | |
| 
 | |
| #include "config.h"
 | |
| 
 | |
| #include <sys/types.h>
 | |
| #include <sys/stat.h>
 | |
| #include <fcntl.h>
 | |
| #include <errno.h>
 | |
| 
 | |
| #include <glib.h>
 | |
| #include <glib/gstdio.h>
 | |
| #include "gcancellable.h"
 | |
| #include "gioerror.h"
 | |
| #include "glocalfileinputstream.h"
 | |
| #include "glocalfileinfo.h"
 | |
| #include "glibintl.h"
 | |
| 
 | |
| #ifdef G_OS_UNIX
 | |
| #include <unistd.h>
 | |
| #include "glib-unix.h"
 | |
| #include "gfiledescriptorbased.h"
 | |
| #endif
 | |
| 
 | |
| #ifdef G_OS_WIN32
 | |
| #include <io.h>
 | |
| #endif
 | |
| 
 | |
| struct _GLocalFileInputStreamPrivate {
 | |
|   int fd;
 | |
|   guint do_close : 1;
 | |
| };
 | |
| 
 | |
| #ifdef G_OS_UNIX
 | |
| static void       g_file_descriptor_based_iface_init   (GFileDescriptorBasedIface *iface);
 | |
| #endif
 | |
| 
 | |
| #define g_local_file_input_stream_get_type _g_local_file_input_stream_get_type
 | |
| #ifdef G_OS_UNIX
 | |
| G_DEFINE_TYPE_WITH_CODE (GLocalFileInputStream, g_local_file_input_stream, G_TYPE_FILE_INPUT_STREAM,
 | |
|                          G_ADD_PRIVATE (GLocalFileInputStream)
 | |
| 			 G_IMPLEMENT_INTERFACE (G_TYPE_FILE_DESCRIPTOR_BASED,
 | |
| 						g_file_descriptor_based_iface_init))
 | |
| #else
 | |
| G_DEFINE_TYPE_WITH_CODE (GLocalFileInputStream, g_local_file_input_stream, G_TYPE_FILE_INPUT_STREAM,
 | |
|                          G_ADD_PRIVATE (GLocalFileInputStream))
 | |
| #endif
 | |
| 
 | |
| static gssize     g_local_file_input_stream_read       (GInputStream      *stream,
 | |
| 							void              *buffer,
 | |
| 							gsize              count,
 | |
| 							GCancellable      *cancellable,
 | |
| 							GError           **error);
 | |
| static gboolean   g_local_file_input_stream_close      (GInputStream      *stream,
 | |
| 							GCancellable      *cancellable,
 | |
| 							GError           **error);
 | |
| static goffset    g_local_file_input_stream_tell       (GFileInputStream  *stream);
 | |
| static gboolean   g_local_file_input_stream_can_seek   (GFileInputStream  *stream);
 | |
| static gboolean   g_local_file_input_stream_seek       (GFileInputStream  *stream,
 | |
| 							goffset            offset,
 | |
| 							GSeekType          type,
 | |
| 							GCancellable      *cancellable,
 | |
| 							GError           **error);
 | |
| static GFileInfo *g_local_file_input_stream_query_info (GFileInputStream  *stream,
 | |
| 							const char        *attributes,
 | |
| 							GCancellable      *cancellable,
 | |
| 							GError           **error);
 | |
| #ifdef G_OS_UNIX
 | |
| static int        g_local_file_input_stream_get_fd     (GFileDescriptorBased *stream);
 | |
| #endif
 | |
| 
 | |
| void
 | |
| _g_local_file_input_stream_set_do_close (GLocalFileInputStream *in,
 | |
| 					  gboolean do_close)
 | |
| {
 | |
|   in->priv->do_close = do_close;
 | |
| }
 | |
| 
 | |
| static void
 | |
| g_local_file_input_stream_class_init (GLocalFileInputStreamClass *klass)
 | |
| {
 | |
|   GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass);
 | |
|   GFileInputStreamClass *file_stream_class = G_FILE_INPUT_STREAM_CLASS (klass);
 | |
| 
 | |
|   stream_class->read_fn = g_local_file_input_stream_read;
 | |
|   stream_class->close_fn = g_local_file_input_stream_close;
 | |
|   file_stream_class->tell = g_local_file_input_stream_tell;
 | |
|   file_stream_class->can_seek = g_local_file_input_stream_can_seek;
 | |
|   file_stream_class->seek = g_local_file_input_stream_seek;
 | |
|   file_stream_class->query_info = g_local_file_input_stream_query_info;
 | |
| }
 | |
| 
 | |
| #ifdef G_OS_UNIX
 | |
| static void
 | |
| g_file_descriptor_based_iface_init (GFileDescriptorBasedIface *iface)
 | |
| {
 | |
|   iface->get_fd = g_local_file_input_stream_get_fd;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static void
 | |
| g_local_file_input_stream_init (GLocalFileInputStream *info)
 | |
| {
 | |
|   info->priv = g_local_file_input_stream_get_instance_private (info);
 | |
|   info->priv->do_close = TRUE;
 | |
| }
 | |
| 
 | |
| GFileInputStream *
 | |
| _g_local_file_input_stream_new (int fd)
 | |
| {
 | |
|   GLocalFileInputStream *stream;
 | |
| 
 | |
|   stream = g_object_new (G_TYPE_LOCAL_FILE_INPUT_STREAM, NULL);
 | |
|   stream->priv->fd = fd;
 | |
|   
 | |
|   return G_FILE_INPUT_STREAM (stream);
 | |
| }
 | |
| 
 | |
| static gssize
 | |
| g_local_file_input_stream_read (GInputStream  *stream,
 | |
| 				void          *buffer,
 | |
| 				gsize          count,
 | |
| 				GCancellable  *cancellable,
 | |
| 				GError       **error)
 | |
| {
 | |
|   GLocalFileInputStream *file;
 | |
|   gssize res;
 | |
| 
 | |
|   file = G_LOCAL_FILE_INPUT_STREAM (stream);
 | |
| 
 | |
|   res = -1;
 | |
|   while (1)
 | |
|     {
 | |
|       if (g_cancellable_set_error_if_cancelled (cancellable, error))
 | |
| 	break;
 | |
|       res = read (file->priv->fd, buffer, count);
 | |
|       if (res == -1)
 | |
| 	{
 | |
|           int errsv = errno;
 | |
| 
 | |
| 	  if (errsv == EINTR)
 | |
| 	    continue;
 | |
| 	  
 | |
| 	  g_set_error (error, G_IO_ERROR,
 | |
| 		       g_io_error_from_errno (errsv),
 | |
| 		       _("Error reading from file: %s"),
 | |
| 		       g_strerror (errsv));
 | |
| 	}
 | |
|       
 | |
|       break;
 | |
|     }
 | |
|   
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| static gboolean
 | |
| g_local_file_input_stream_close (GInputStream  *stream,
 | |
| 				 GCancellable  *cancellable,
 | |
| 				 GError       **error)
 | |
| {
 | |
|   GLocalFileInputStream *file;
 | |
| 
 | |
|   file = G_LOCAL_FILE_INPUT_STREAM (stream);
 | |
| 
 | |
|   if (!file->priv->do_close)
 | |
|     return TRUE;
 | |
| 
 | |
|   if (file->priv->fd == -1)
 | |
|     return TRUE;
 | |
| 
 | |
|   if (!g_close (file->priv->fd, NULL))
 | |
|     {
 | |
|       int errsv = errno;
 | |
|       
 | |
|       g_set_error (error, G_IO_ERROR,
 | |
|                    g_io_error_from_errno (errsv),
 | |
|                    _("Error closing file: %s"),
 | |
|                    g_strerror (errsv));
 | |
|       return FALSE;
 | |
|     }
 | |
| 
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| 
 | |
| static goffset
 | |
| g_local_file_input_stream_tell (GFileInputStream *stream)
 | |
| {
 | |
|   GLocalFileInputStream *file;
 | |
|   off_t pos;
 | |
| 
 | |
|   file = G_LOCAL_FILE_INPUT_STREAM (stream);
 | |
|   
 | |
|   pos = lseek (file->priv->fd, 0, SEEK_CUR);
 | |
| 
 | |
|   if (pos == (off_t)-1)
 | |
|     return 0;
 | |
|   
 | |
|   return pos;
 | |
| }
 | |
| 
 | |
| static gboolean
 | |
| g_local_file_input_stream_can_seek (GFileInputStream *stream)
 | |
| {
 | |
|   GLocalFileInputStream *file;
 | |
|   off_t pos;
 | |
| 
 | |
|   file = G_LOCAL_FILE_INPUT_STREAM (stream);
 | |
|   
 | |
|   pos = lseek (file->priv->fd, 0, SEEK_CUR);
 | |
| 
 | |
|   if (pos == (off_t)-1 && errno == ESPIPE)
 | |
|     return FALSE;
 | |
|   
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| static int
 | |
| seek_type_to_lseek (GSeekType type)
 | |
| {
 | |
|   switch (type)
 | |
|     {
 | |
|     default:
 | |
|     case G_SEEK_CUR:
 | |
|       return SEEK_CUR;
 | |
|       
 | |
|     case G_SEEK_SET:
 | |
|       return SEEK_SET;
 | |
|       
 | |
|     case G_SEEK_END:
 | |
|       return SEEK_END;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static gboolean
 | |
| g_local_file_input_stream_seek (GFileInputStream  *stream,
 | |
| 				goffset            offset,
 | |
| 				GSeekType          type,
 | |
| 				GCancellable      *cancellable,
 | |
| 				GError           **error)
 | |
| {
 | |
|   GLocalFileInputStream *file;
 | |
|   off_t pos;
 | |
| 
 | |
|   file = G_LOCAL_FILE_INPUT_STREAM (stream);
 | |
| 
 | |
|   pos = lseek (file->priv->fd, offset, seek_type_to_lseek (type));
 | |
| 
 | |
|   if (pos == (off_t)-1)
 | |
|     {
 | |
|       int errsv = errno;
 | |
| 
 | |
|       g_set_error (error, G_IO_ERROR,
 | |
| 		   g_io_error_from_errno (errsv),
 | |
| 		   _("Error seeking in file: %s"),
 | |
| 		   g_strerror (errsv));
 | |
|       return FALSE;
 | |
|     }
 | |
|   
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| static GFileInfo *
 | |
| g_local_file_input_stream_query_info (GFileInputStream  *stream,
 | |
| 				      const char        *attributes,
 | |
| 				      GCancellable      *cancellable,
 | |
| 				      GError           **error)
 | |
| {
 | |
|   GLocalFileInputStream *file;
 | |
| 
 | |
|   file = G_LOCAL_FILE_INPUT_STREAM (stream);
 | |
| 
 | |
|   if (g_cancellable_set_error_if_cancelled (cancellable, error))
 | |
|     return NULL;
 | |
|   
 | |
|   return _g_local_file_info_get_from_fd (file->priv->fd,
 | |
| 					 attributes,
 | |
| 					 error);
 | |
| }
 | |
| 
 | |
| #ifdef G_OS_UNIX
 | |
| static int
 | |
| g_local_file_input_stream_get_fd (GFileDescriptorBased *fd_based)
 | |
| {
 | |
|   GLocalFileInputStream *stream = G_LOCAL_FILE_INPUT_STREAM (fd_based);
 | |
|   return stream->priv->fd;
 | |
| }
 | |
| #endif
 |