summaryrefslogtreecommitdiff
path: root/libmailutils/stream/rdcache_stream.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmailutils/stream/rdcache_stream.c')
-rw-r--r--libmailutils/stream/rdcache_stream.c212
1 files changed, 212 insertions, 0 deletions
diff --git a/libmailutils/stream/rdcache_stream.c b/libmailutils/stream/rdcache_stream.c
new file mode 100644
index 000000000..c1d0013dd
--- /dev/null
+++ b/libmailutils/stream/rdcache_stream.c
@@ -0,0 +1,212 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2009, 2010 Free Software Foundation, 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 3, 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 GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <errno.h>
+#include <mailutils/types.h>
+#include <mailutils/sys/rdcache_stream.h>
+
+size_t mu_rdcache_stream_max_memory_size = 4096;
+
+static int
+rdcache_read (struct _mu_stream *str, char *buf, size_t size, size_t *pnbytes)
+{
+ struct _mu_rdcache_stream *sp = (struct _mu_rdcache_stream *) str;
+ int status = 0;
+ size_t nbytes = 0;
+
+ if (sp->offset < sp->size)
+ {
+ status = mu_stream_read (sp->cache, buf, size, &nbytes);
+ if (status)
+ return status;
+ sp->offset += nbytes;
+ sp->size += nbytes;
+ buf += nbytes;
+ size -= nbytes;
+ }
+ else if (sp->offset > sp->size)
+ {
+ size_t left = sp->offset - sp->size;
+ status = mu_stream_seek (sp->cache, 0, MU_SEEK_END, NULL);
+ if (status)
+ return status;
+ status = mu_stream_copy (sp->cache, sp->transport, left, NULL);
+ if (status)
+ return status;
+ sp->size = sp->offset;
+ }
+
+ if (size)
+ {
+ size_t rdbytes;
+ status = mu_stream_read (sp->transport, buf, size, &rdbytes);
+ if (rdbytes)
+ {
+ int rc;
+
+ sp->offset += rdbytes;
+ sp->size += rdbytes;
+ nbytes += rdbytes;
+ rc = mu_stream_write (sp->cache, buf, rdbytes, NULL);
+ if (rc)
+ {
+ if (status == 0)
+ status = rc;
+ }
+ }
+ }
+ if (pnbytes)
+ *pnbytes = nbytes;
+ return status;
+}
+
+static int
+rdcache_size (struct _mu_stream *str, off_t *psize)
+{
+ struct _mu_rdcache_stream *sp = (struct _mu_rdcache_stream *) str;
+ *psize = sp->size;
+ return 0;
+}
+
+static int
+rdcache_seek (struct _mu_stream *str, mu_off_t off, mu_off_t *presult)
+{
+ struct _mu_rdcache_stream *sp = (struct _mu_rdcache_stream *) str;
+
+ if (off < 0)
+ return ESPIPE;
+
+ if (off < sp->size)
+ {
+ int status = mu_stream_seek (sp->cache, off, MU_SEEK_SET, NULL);
+ if (status)
+ return status;
+ }
+
+ sp->offset = off;
+ *presult = sp->offset;
+ return 0;
+}
+
+static int
+rdcache_wait (struct _mu_stream *str, int *pflags, struct timeval *tvp)
+{
+ struct _mu_rdcache_stream *sp = (struct _mu_rdcache_stream *) str;
+ return mu_stream_wait (sp->transport, pflags, tvp);
+}
+
+/* FIXME: Truncate? */
+
+static int
+rdcache_ioctl (struct _mu_stream *str, int op, void *arg)
+{
+ struct _mu_rdcache_stream *sp = (struct _mu_rdcache_stream *) str;
+ mu_transport_t *ptrans;
+
+ switch (op)
+ {
+ case MU_IOCTL_GET_TRANSPORT:
+ if (!arg)
+ return EINVAL;
+ ptrans = arg;
+ ptrans[0] = (mu_transport_t) sp->transport;
+ ptrans[1] = NULL;
+ break;
+
+ case MU_IOCTL_GET_TRANSPORT_BUFFER:
+ case MU_IOCTL_SET_TRANSPORT_BUFFER:
+ if (!arg)
+ return EINVAL;
+ else
+ {
+ struct mu_buffer_query *qp = arg;
+ if (qp->type != MU_TRANSPORT_INPUT || !sp->transport)
+ return EINVAL;
+ return mu_stream_ioctl (sp->transport, op, arg);
+ }
+
+ default:
+ return ENOSYS;
+ }
+ return 0;
+}
+
+static int
+rdcache_open (struct _mu_stream *str)
+{
+ struct _mu_rdcache_stream *sp = (struct _mu_rdcache_stream *) str;
+ return mu_stream_open (sp->transport);
+}
+
+static int
+rdcache_close (struct _mu_stream *str)
+{
+ struct _mu_rdcache_stream *sp = (struct _mu_rdcache_stream *) str;
+ return mu_stream_close (sp->transport);
+}
+
+static void
+rdcache_done (struct _mu_stream *str)
+{
+ struct _mu_rdcache_stream *sp = (struct _mu_rdcache_stream *) str;
+ mu_stream_unref (sp->transport);
+ mu_stream_unref (sp->cache);
+}
+
+int
+mu_rdcache_stream_create (mu_stream_t *pstream, mu_stream_t transport,
+ int flags)
+{
+ struct _mu_rdcache_stream *sp;
+ int rc;
+ int sflags = MU_STREAM_READ | MU_STREAM_SEEK | (flags & MU_STREAM_AUTOCLOSE);
+
+ if (flags & ~sflags)
+ return EINVAL;
+
+ sp = (struct _mu_rdcache_stream *)
+ _mu_stream_create (sizeof (*sp), sflags | _MU_STR_OPEN);
+ if (!sp)
+ return ENOMEM;
+
+ sp->stream.read = rdcache_read;
+ sp->stream.open = rdcache_open;
+ sp->stream.close = rdcache_close;
+ sp->stream.done = rdcache_done;
+ sp->stream.seek = rdcache_seek;
+ sp->stream.size = rdcache_size;
+ sp->stream.ctl = rdcache_ioctl;
+ sp->stream.wait = rdcache_wait;
+
+ if (!(flags & MU_STREAM_AUTOCLOSE))
+ mu_stream_ref (transport);
+ sp->transport = transport;
+
+ if ((rc = mu_memory_stream_create (&sp->cache, MU_STREAM_RDWR)))
+ {
+ mu_stream_destroy ((mu_stream_t*) &sp);
+ return rc;
+ }
+
+ *pstream = (mu_stream_t) sp;
+ return 0;
+}
+

Return to:

Send suggestions and report system problems to the System administrator.