Commit 2203753f 2203753f72c89a194889baa199a513e3ed02e5b3 by Sergey Poznyakoff

Implement seek on filters in read-only mode.

* include/mailutils/stream.h (mu_stream_skip_input_bytes): New proto.
* mailbox/stream.c (mu_stream_skip_input_bytes): New function.
* mailbox/fltstream.c (filter_seek): Re-implement on top of
mu_stream_skip_input_bytes.
(mu_filter_stream_create): Fix flag validity checking.

* examples/base64.c: Add new option (-s) for testing seek operations
on filters.
1 parent 0f2fc18a
......@@ -53,8 +53,9 @@ main (int argc, char * argv [])
int flags = MU_STREAM_READ;
char *input = NULL, *output = NULL;
char *encoding = "base64";
mu_off_t shift = 0;
while ((c = getopt (argc, argv, "deE:hi:o:pvw")) != EOF)
while ((c = getopt (argc, argv, "deE:hi:o:ps:vw")) != EOF)
switch (c)
{
case 'i':
......@@ -81,6 +82,10 @@ main (int argc, char * argv [])
printable = 1; /* FIXME: Not implemented */
break;
case 's':
shift = strtoul (optarg, NULL, 0);
break;
case 'v':
verbose = 1;
break;
......@@ -98,7 +103,7 @@ main (int argc, char * argv [])
}
if (input)
MU_ASSERT (mu_file_stream_create (&in, input, MU_STREAM_READ));
MU_ASSERT (mu_file_stream_create (&in, input, MU_STREAM_READ|MU_STREAM_SEEK));
else
MU_ASSERT (mu_stdio_stream_create (&in, MU_STDIN_FD, 0));
MU_ASSERT (mu_stream_open (in));
......@@ -112,13 +117,18 @@ main (int argc, char * argv [])
if (flags == MU_STREAM_READ)
{
MU_ASSERT (mu_filter_create (&flt, in, encoding, mode, MU_STREAM_READ));
MU_ASSERT (mu_filter_create (&flt, in, encoding, mode,
MU_STREAM_READ|MU_STREAM_SEEK));
if (shift)
MU_ASSERT (mu_stream_seek (flt, shift, MU_SEEK_SET, NULL));
c_copy (out, flt);
}
else
{
MU_ASSERT (mu_filter_create (&flt, out, encoding, mode,
MU_STREAM_WRITE));
if (shift)
MU_ASSERT (mu_stream_seek (in, shift, MU_SEEK_SET, NULL));
c_copy (flt, in);
}
......
......@@ -71,6 +71,9 @@ void mu_stream_clearerr (mu_stream_t stream);
int mu_stream_eof (mu_stream_t stream);
int mu_stream_seek (mu_stream_t stream, mu_off_t offset, int whence,
mu_off_t *pres);
int mu_stream_skip_input_bytes (mu_stream_t stream, mu_off_t count,
mu_off_t *pres);
int mu_stream_set_buffer (mu_stream_t stream, enum mu_buffer_type type,
size_t size);
int mu_stream_read (mu_stream_t stream, void *buf, size_t size, size_t *pread);
......
......@@ -313,12 +313,17 @@ filter_wr_flush (mu_stream_t stream)
return rc;
}
/* FIXME: Seeks in the *transport* stream */
static int
filter_seek (struct _mu_stream *stream, mu_off_t off, mu_off_t *ppos)
{
struct _mu_filter_stream *fs = (struct _mu_filter_stream *)stream;
return mu_stream_seek (fs->transport, off, MU_SEEK_SET, ppos);
int status;
status = mu_stream_seek (fs->transport, 0, MU_SEEK_SET, NULL);
if (status)
return status;
stream->offset = 0;
return mu_stream_skip_input_bytes (stream, off, ppos);
}
static int
......@@ -392,7 +397,7 @@ mu_filter_stream_create (mu_stream_t *pflt,
if ((flags & MU_STREAM_RDWR) == MU_STREAM_RDWR
|| !(flags & MU_STREAM_RDWR)
|| (flags & MU_STREAM_SEEK))
|| (flags & (MU_STREAM_WRITE|MU_STREAM_SEEK)) == (MU_STREAM_WRITE|MU_STREAM_SEEK))
return EINVAL;
fs = (struct _mu_filter_stream *) _mu_stream_create (sizeof (*fs), flags);
......
......@@ -334,6 +334,71 @@ mu_stream_seek (mu_stream_t stream, mu_off_t offset, int whence,
return 0;
}
/* Skip COUNT bytes from the current position in stream by reading from
it. Return new offset in PRES.
Return 0 on success, EACCES if STREAM was not opened for reading.
Another non-zero exit codes are propagated from the underlying
input operations.
This function is designed to help implement seek method in otherwise
unseekable streams (such as filters). Do not use it if you absolutely
have to. Using it on an unbuffered stream is a terrible waste of CPU. */
int
mu_stream_skip_input_bytes (mu_stream_t stream, mu_off_t count, mu_off_t *pres)
{
mu_off_t pos;
int rc;
if (!(stream->flags & MU_STREAM_READ))
return _stream_seterror (stream, EACCES, 1);
if (stream->buftype == mu_buffer_none)
{
for (pos = 0; pos < count; pos++)
{
char c;
size_t nrd;
rc = mu_stream_read (stream, &c, 1, &nrd);
if (nrd == 0)
rc = ESPIPE;
if (rc)
break;
}
}
else
{
if ((rc = _stream_flush_buffer (stream, 1)))
return rc;
for (pos = 0;;)
{
if (stream->level == 0)
{
rc = _stream_fill_buffer (stream);
if (rc)
break;
if (stream->level == 0)
{
rc = ESPIPE;
break;
}
}
if (pos <= count && count < pos + stream->level)
{
rc = 0;
stream->cur = stream->buffer + count - pos;
pos = count;
break;
}
}
}
stream->offset += pos;
if (pres)
*pres = stream->offset;
return rc;
}
int
mu_stream_set_buffer (mu_stream_t stream, enum mu_buffer_type type,
size_t size)
......