/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 2009-2021 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 . */
#ifdef HAVE_CONFIG_H
# include
#endif
#include
#include
#include
#include
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 code, int opcode, void *arg)
{
struct _mu_rdcache_stream *sp = (struct _mu_rdcache_stream *) str;
switch (code)
{
case MU_IOCTL_TRANSPORT:
if (!arg)
return EINVAL;
else
{
mu_transport_t *ptrans = arg;
switch (opcode)
{
case MU_IOCTL_OP_GET:
ptrans[0] = (mu_transport_t) sp->transport;
ptrans[1] = NULL;
break;
case MU_IOCTL_OP_SET:
return ENOSYS;
default:
return EINVAL;
}
}
break;
case MU_IOCTL_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, code, opcode, arg);
}
default:
return mu_stream_ioctl (sp->transport, code, opcode, arg);
}
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;
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;
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;
}