summaryrefslogtreecommitdiff
path: root/libmailutils/streamref.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmailutils/streamref.c')
-rw-r--r--libmailutils/streamref.c312
1 files changed, 312 insertions, 0 deletions
diff --git a/libmailutils/streamref.c b/libmailutils/streamref.c
new file mode 100644
index 000000000..83f401ebb
--- /dev/null
+++ b/libmailutils/streamref.c
@@ -0,0 +1,312 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 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/stream.h>
+#include <mailutils/errno.h>
+#include <mailutils/sys/streamref.h>
+
+#define _MU_STR_ERRMASK (_MU_STR_ERR|_MU_STR_EOF)
+
+static int
+streamref_return (struct _mu_streamref *sp, int rc)
+{
+ if (rc)
+ sp->stream.last_err = sp->transport->last_err;
+ sp->stream.flags = (sp->stream.flags & ~_MU_STR_ERRMASK) |
+ (sp->transport->flags & _MU_STR_ERRMASK);
+ return rc;
+}
+
+static int
+_streamref_read (struct _mu_stream *str, char *buf, size_t bufsize,
+ size_t *pnread)
+{
+ struct _mu_streamref *sp = (struct _mu_streamref *)str;
+ int rc;
+ size_t nread;
+ mu_off_t off;
+
+ rc = mu_stream_seek (sp->transport, sp->offset, MU_SEEK_SET, &off);
+ if (rc == 0)
+ {
+ if (sp->end)
+ {
+ size_t size = sp->end - off + 1;
+ if (size < bufsize)
+ bufsize = size;
+ }
+ rc = mu_stream_read (sp->transport, buf, bufsize, &nread);
+ if (rc == 0)
+ {
+ sp->offset += nread;
+ *pnread = nread;
+ }
+ }
+ else if (rc == ESPIPE)
+ {
+ *pnread = 0;
+ mu_stream_clearerr (sp->transport);
+ return 0;
+ }
+ return streamref_return (sp, rc);
+}
+
+static int
+_streamref_readdelim (struct _mu_stream *str, char *buf, size_t bufsize,
+ int delim, size_t *pnread)
+{
+ struct _mu_streamref *sp = (struct _mu_streamref *)str;
+ int rc;
+ size_t nread;
+ mu_off_t off;
+
+ rc = mu_stream_seek (sp->transport, sp->offset, MU_SEEK_SET, &off);
+ if (rc == 0)
+ {
+ rc = mu_stream_readdelim (sp->transport, buf, bufsize, delim, &nread);
+ if (rc == 0)
+ {
+ if (sp->end)
+ {
+ size_t size = sp->end - off + 1;
+ if (nread > size)
+ nread = size;
+ }
+ sp->offset += nread;
+ *pnread = nread;
+ }
+ }
+ else if (rc == ESPIPE)
+ {
+ *pnread = 0;
+ mu_stream_clearerr (sp->transport);
+ return 0;
+ }
+ return streamref_return (sp, rc);
+}
+
+static int
+_streamref_write (struct _mu_stream *str, const char *buf, size_t bufsize,
+ size_t *pnwrite)
+{
+ struct _mu_streamref *sp = (struct _mu_streamref *)str;
+ int rc;
+ size_t nwrite;
+ rc = mu_stream_seek (sp->transport, sp->offset, MU_SEEK_SET, NULL);
+ if (rc == 0)
+ {
+ rc = mu_stream_write (sp->transport, buf, bufsize, &nwrite);
+ if (rc == 0)
+ {
+ sp->offset += nwrite;
+ *pnwrite = nwrite;
+ }
+ }
+ return streamref_return (sp, rc);
+}
+
+static int
+_streamref_flush (struct _mu_stream *str)
+{
+ struct _mu_streamref *sp = (struct _mu_streamref *)str;
+ return streamref_return (sp, mu_stream_flush (sp->transport));
+}
+
+static int
+_streamref_open (struct _mu_stream *str)
+{
+ struct _mu_streamref *sp = (struct _mu_streamref *)str;
+ return streamref_return (sp, mu_stream_open (sp->transport));
+}
+
+static int
+_streamref_close (struct _mu_stream *str)
+{
+ struct _mu_streamref *sp = (struct _mu_streamref *)str;
+ return streamref_return (sp, mu_stream_close (sp->transport));
+}
+
+static void
+_streamref_done (struct _mu_stream *str)
+{
+ struct _mu_streamref *sp = (struct _mu_streamref *)str;
+ mu_stream_unref (sp->transport);
+}
+
+static int
+_streamref_seek (struct _mu_stream *str, mu_off_t off, mu_off_t *ppos)
+{
+ struct _mu_streamref *sp = (struct _mu_streamref *)str;
+ mu_off_t size;
+ int rc;
+
+ if (sp->end)
+ size = sp->end - sp->start + 1;
+ else
+ {
+ rc = mu_stream_size (sp->transport, &size);
+ if (rc)
+ return streamref_return (sp, rc);
+ size -= sp->start;
+ }
+
+ if (off < 0 || off > size)
+ return sp->stream.last_err = ESPIPE;
+ rc = mu_stream_seek (sp->transport, sp->start + off, MU_SEEK_SET,
+ &sp->offset);
+ if (rc)
+ return streamref_return (sp, rc);
+ *ppos = sp->offset - sp->start;
+ return 0;
+}
+
+static int
+_streamref_size (struct _mu_stream *str, mu_off_t *psize)
+{
+ struct _mu_streamref *sp = (struct _mu_streamref *)str;
+ mu_off_t size;
+ int rc = 0;
+
+ if (sp->end)
+ size = sp->end - sp->start + 1;
+ else
+ {
+ rc = mu_stream_size (sp->transport, &size);
+ if (rc)
+ return streamref_return (sp, rc);
+ size -= sp->start;
+ }
+ if (rc == 0)
+ *psize = size;
+ return rc;
+}
+
+static int
+_streamref_ctl (struct _mu_stream *str, int op, void *arg)
+{
+ struct _mu_streamref *sp = (struct _mu_streamref *)str;
+ mu_off_t *lim;
+
+ switch (op)
+ {
+ case MU_IOCTL_ABRIDGE_SEEK:
+ if (!arg)
+ return EINVAL;
+ lim = arg;
+ sp->start = lim[0];
+ sp->end = lim[1];
+ return 0;
+
+ case MU_IOCTL_GET_SEEK_LIMITS:
+ if (!arg)
+ return EINVAL;
+ lim = arg;
+ lim[0] = sp->start;
+ lim[1] = sp->end;
+ return 0;
+ }
+ return streamref_return (sp, mu_stream_ioctl (sp->transport, op, arg));
+}
+
+static int
+_streamref_wait (struct _mu_stream *str, int *pflags, struct timeval *tvp)
+{
+ struct _mu_streamref *sp = (struct _mu_streamref *)str;
+ return streamref_return (sp, mu_stream_wait (sp->transport, pflags, tvp));
+}
+
+static int
+_streamref_truncate (struct _mu_stream *str, mu_off_t size)
+{
+ struct _mu_streamref *sp = (struct _mu_streamref *)str;
+ return streamref_return (sp, mu_stream_truncate (sp->transport, size));
+}
+
+static int
+_streamref_shutdown (struct _mu_stream *str, int how)
+{
+ struct _mu_streamref *sp = (struct _mu_streamref *)str;
+ return streamref_return (sp, mu_stream_shutdown (sp->transport, how));
+}
+
+static const char *
+_streamref_error_string (struct _mu_stream *str, int rc)
+{
+ struct _mu_streamref *sp = (struct _mu_streamref *)str;
+ const char *p = mu_stream_strerror (sp->transport, rc);
+ if (!p)
+ p = mu_strerror (rc);
+ return p;
+}
+
+int
+mu_streamref_create_abridged (mu_stream_t *pref, mu_stream_t str,
+ mu_off_t start, mu_off_t end)
+{
+ int rc;
+ mu_off_t off;
+ int flags;
+ struct _mu_streamref *sp;
+
+ rc = mu_stream_seek (str, 0, MU_SEEK_SET, &off);
+ if (rc)
+ return rc;
+ mu_stream_get_flags (str, &flags);
+ sp = (struct _mu_streamref *)
+ _mu_stream_create (sizeof (*sp), flags & ~MU_STREAM_AUTOCLOSE);
+ if (!sp)
+ return ENOMEM;
+
+ mu_stream_ref (str);
+
+ sp->stream.read = _streamref_read;
+ if (str->readdelim)
+ sp->stream.readdelim = _streamref_readdelim;
+ sp->stream.write = _streamref_write;
+ sp->stream.flush = _streamref_flush;
+ sp->stream.open = _streamref_open;
+ sp->stream.close = _streamref_close;
+ sp->stream.done = _streamref_done;
+ sp->stream.seek = _streamref_seek;
+ sp->stream.size = _streamref_size;
+ sp->stream.ctl = _streamref_ctl;
+ sp->stream.wait = _streamref_wait;
+ sp->stream.truncate = _streamref_truncate;
+ sp->stream.shutdown = _streamref_shutdown;
+ sp->stream.error_string = _streamref_error_string;
+
+ sp->transport = str;
+ sp->start = start;
+ sp->end = end;
+ if (off < start || off > end)
+ off = start;
+ sp->offset = off;
+ *pref = (mu_stream_t) sp;
+ return 0;
+}
+
+int
+mu_streamref_create (mu_stream_t *pref, mu_stream_t str)
+{
+ return mu_streamref_create_abridged (pref, str, 0, 0);
+}
+

Return to:

Send suggestions and report system problems to the System administrator.