1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2000-2012 Jeffrey Stedfast
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public License
7 * as published by the Free Software Foundation; either version 2.1
8 * of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free
17 * Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
28 #include <sys/types.h>
34 #include "gmime-stream-fs.h"
38 /* _commit() is the equivalent of fsync() on Windows, but it aborts the
39 * program if the fd is a tty, so we'll just no-op for now... */
40 static int fsync (int fd) { return 0; }
42 static int fsync (int fd) { return 0; }
48 * SECTION: gmime-stream-fs
49 * @title: GMimeStreamFs
50 * @short_description: A low-level FileSystem stream
51 * @see_also: #GMimeStream
53 * A simple #GMimeStream implementation that sits on top of the
54 * low-level UNIX file descriptor based I/O layer.
58 static void g_mime_stream_fs_class_init (GMimeStreamFsClass *klass);
59 static void g_mime_stream_fs_init (GMimeStreamFs *stream, GMimeStreamFsClass *klass);
60 static void g_mime_stream_fs_finalize (GObject *object);
62 static ssize_t stream_read (GMimeStream *stream, char *buf, size_t len);
63 static ssize_t stream_write (GMimeStream *stream, const char *buf, size_t len);
64 static int stream_flush (GMimeStream *stream);
65 static int stream_close (GMimeStream *stream);
66 static gboolean stream_eos (GMimeStream *stream);
67 static int stream_reset (GMimeStream *stream);
68 static gint64 stream_seek (GMimeStream *stream, gint64 offset, GMimeSeekWhence whence);
69 static gint64 stream_tell (GMimeStream *stream);
70 static gint64 stream_length (GMimeStream *stream);
71 static GMimeStream *stream_substream (GMimeStream *stream, gint64 start, gint64 end);
74 static GMimeStreamClass *parent_class = NULL;
78 g_mime_stream_fs_get_type (void)
80 static GType type = 0;
83 static const GTypeInfo info = {
84 sizeof (GMimeStreamFsClass),
85 NULL, /* base_class_init */
86 NULL, /* base_class_finalize */
87 (GClassInitFunc) g_mime_stream_fs_class_init,
88 NULL, /* class_finalize */
89 NULL, /* class_data */
90 sizeof (GMimeStreamFs),
92 (GInstanceInitFunc) g_mime_stream_fs_init,
95 type = g_type_register_static (GMIME_TYPE_STREAM, "GMimeStreamFs", &info, 0);
103 g_mime_stream_fs_class_init (GMimeStreamFsClass *klass)
105 GMimeStreamClass *stream_class = GMIME_STREAM_CLASS (klass);
106 GObjectClass *object_class = G_OBJECT_CLASS (klass);
108 parent_class = g_type_class_ref (GMIME_TYPE_STREAM);
110 object_class->finalize = g_mime_stream_fs_finalize;
112 stream_class->read = stream_read;
113 stream_class->write = stream_write;
114 stream_class->flush = stream_flush;
115 stream_class->close = stream_close;
116 stream_class->eos = stream_eos;
117 stream_class->reset = stream_reset;
118 stream_class->seek = stream_seek;
119 stream_class->tell = stream_tell;
120 stream_class->length = stream_length;
121 stream_class->substream = stream_substream;
125 g_mime_stream_fs_init (GMimeStreamFs *stream, GMimeStreamFsClass *klass)
127 stream->owner = TRUE;
133 g_mime_stream_fs_finalize (GObject *object)
135 GMimeStreamFs *stream = (GMimeStreamFs *) object;
137 if (stream->owner && stream->fd != -1)
140 G_OBJECT_CLASS (parent_class)->finalize (object);
144 stream_read (GMimeStream *stream, char *buf, size_t len)
146 GMimeStreamFs *fs = (GMimeStreamFs *) stream;
154 if (stream->bound_end != -1 && stream->position >= stream->bound_end) {
159 if (stream->bound_end != -1)
160 len = (size_t) MIN (stream->bound_end - stream->position, (gint64) len);
162 /* make sure we are at the right position */
163 lseek (fs->fd, (off_t) stream->position, SEEK_SET);
166 nread = read (fs->fd, buf, len);
167 } while (nread == -1 && errno == EINTR);
170 stream->position += nread;
171 } else if (nread == 0) {
179 stream_write (GMimeStream *stream, const char *buf, size_t len)
181 GMimeStreamFs *fs = (GMimeStreamFs *) stream;
190 if (stream->bound_end != -1 && stream->position >= stream->bound_end) {
195 if (stream->bound_end != -1)
196 len = (size_t) MIN (stream->bound_end - stream->position, (gint64) len);
198 /* make sure we are at the right position */
199 lseek (fs->fd, (off_t) stream->position, SEEK_SET);
203 n = write (fs->fd, buf + nwritten, len - nwritten);
204 } while (n == -1 && (errno == EINTR || errno == EAGAIN));
208 } while (n != -1 && nwritten < len);
210 if (n == -1 && (errno == EFBIG || errno == ENOSPC))
214 stream->position += nwritten;
215 } else if (n == -1) {
216 /* error and nothing written */
224 stream_flush (GMimeStream *stream)
226 GMimeStreamFs *fs = (GMimeStreamFs *) stream;
233 return fsync (fs->fd);
237 stream_close (GMimeStream *stream)
239 GMimeStreamFs *fs = (GMimeStreamFs *) stream;
246 if ((rv = close (fs->fd)) == 0)
248 } while (rv == -1 && errno == EINTR);
254 stream_eos (GMimeStream *stream)
256 GMimeStreamFs *fs = (GMimeStreamFs *) stream;
265 stream_reset (GMimeStream *stream)
267 GMimeStreamFs *fs = (GMimeStreamFs *) stream;
274 if (stream->position == stream->bound_start) {
279 /* FIXME: if stream_read/write is always going to lseek to
280 * make sure fd's seek position matches our own, we could just
281 * set stream->position = stream->bound_start and be done. */
282 if (lseek (fs->fd, (off_t) stream->bound_start, SEEK_SET) == -1)
291 stream_seek (GMimeStream *stream, gint64 offset, GMimeSeekWhence whence)
293 GMimeStreamFs *fs = (GMimeStreamFs *) stream;
302 case GMIME_STREAM_SEEK_SET:
305 case GMIME_STREAM_SEEK_CUR:
306 real = stream->position + offset;
308 case GMIME_STREAM_SEEK_END:
309 if (offset > 0 || (stream->bound_end == -1 && !fs->eos)) {
310 /* need to do an actual lseek() here because
311 * we either don't know the offset of the end
312 * of the stream and/or don't know if we can
313 * seek past the end */
314 if ((real = lseek (fs->fd, (off_t) offset, SEEK_END)) == -1)
316 } else if (fs->eos && stream->bound_end == -1) {
317 /* seeking backwards from eos (which happens
318 * to be our current position) */
319 real = stream->position + offset;
321 /* seeking backwards from a known position */
322 real = stream->bound_end + offset;
327 g_assert_not_reached ();
331 /* sanity check the resultant offset */
332 if (real < stream->bound_start) {
337 /* short-cut if we are seeking to our current position */
338 if (real == stream->position)
341 if (stream->bound_end != -1 && real > stream->bound_end) {
346 if ((real = lseek (fs->fd, (off_t) real, SEEK_SET)) == -1)
349 /* reset eos if appropriate */
350 if ((stream->bound_end != -1 && real < stream->bound_end) ||
351 (fs->eos && real < stream->position))
354 stream->position = real;
360 stream_tell (GMimeStream *stream)
362 return stream->position;
366 stream_length (GMimeStream *stream)
368 GMimeStreamFs *fs = (GMimeStreamFs *) stream;
376 if (stream->bound_end != -1)
377 return stream->bound_end - stream->bound_start;
379 bound_end = lseek (fs->fd, (off_t) 0, SEEK_END);
380 lseek (fs->fd, (off_t) stream->position, SEEK_SET);
382 if (bound_end < stream->bound_start) {
387 return bound_end - stream->bound_start;
391 stream_substream (GMimeStream *stream, gint64 start, gint64 end)
395 fs = g_object_newv (GMIME_TYPE_STREAM_FS, 0, NULL);
396 g_mime_stream_construct (GMIME_STREAM (fs), start, end);
397 fs->fd = GMIME_STREAM_FS (stream)->fd;
401 return (GMimeStream *) fs;
406 * g_mime_stream_fs_new:
407 * @fd: a file descriptor
409 * Creates a new #GMimeStreamFs object around @fd.
411 * Returns: a stream using @fd.
414 g_mime_stream_fs_new (int fd)
420 _setmode (fd, O_BINARY);
423 if ((start = lseek (fd, (off_t) 0, SEEK_CUR)) == -1)
426 fs = g_object_newv (GMIME_TYPE_STREAM_FS, 0, NULL);
427 g_mime_stream_construct (GMIME_STREAM (fs), start, -1);
432 return (GMimeStream *) fs;
437 * g_mime_stream_fs_new_with_bounds:
438 * @fd: a file descriptor
439 * @start: start boundary
442 * Creates a new #GMimeStreamFs object around @fd with bounds @start
445 * Returns: a stream using @fd with bounds @start and @end.
448 g_mime_stream_fs_new_with_bounds (int fd, gint64 start, gint64 end)
453 _setmode (fd, O_BINARY);
456 fs = g_object_newv (GMIME_TYPE_STREAM_FS, 0, NULL);
457 g_mime_stream_construct (GMIME_STREAM (fs), start, end);
462 return (GMimeStream *) fs;
467 * g_mime_stream_fs_get_owner:
468 * @stream: a #GMimeStreamFs
470 * Gets whether or not @stream owns the backend file descriptor.
472 * Returns: %TRUE if @stream owns the backend file descriptor or %FALSE
476 g_mime_stream_fs_get_owner (GMimeStreamFs *stream)
478 g_return_val_if_fail (GMIME_IS_STREAM_FS (stream), FALSE);
480 return stream->owner;
485 * g_mime_stream_fs_set_owner:
486 * @stream: a #GMimeStreamFs
487 * @owner: %TRUE if this stream should own the file descriptor or %FALSE otherwise
489 * Sets whether or not @stream owns the backend file descriptor.
491 * Note: @owner should be %TRUE if the stream should close() the
492 * backend file descriptor when destroyed or %FALSE otherwise.
495 g_mime_stream_fs_set_owner (GMimeStreamFs *stream, gboolean owner)
497 g_return_if_fail (GMIME_IS_STREAM_FS (stream));
499 stream->owner = owner;