stream.c revision 156230
1156230Smux/*-
2156230Smux * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
3156230Smux * All rights reserved.
4156230Smux *
5156230Smux * Redistribution and use in source and binary forms, with or without
6156230Smux * modification, are permitted provided that the following conditions
7156230Smux * are met:
8156230Smux * 1. Redistributions of source code must retain the above copyright
9156230Smux *    notice, this list of conditions and the following disclaimer.
10156230Smux * 2. Redistributions in binary form must reproduce the above copyright
11156230Smux *    notice, this list of conditions and the following disclaimer in the
12156230Smux *    documentation and/or other materials provided with the distribution.
13156230Smux *
14156230Smux * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15156230Smux * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16156230Smux * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17156230Smux * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18156230Smux * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19156230Smux * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20156230Smux * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21156230Smux * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22156230Smux * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23156230Smux * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24156230Smux * SUCH DAMAGE.
25156230Smux *
26156230Smux * $FreeBSD: vendor/csup/dist/contrib/csup/stream.c 156230 2006-03-03 04:11:29Z mux $
27156230Smux */
28156230Smux
29156230Smux#include <sys/types.h>
30156230Smux#include <sys/stat.h>
31156230Smux
32156230Smux#include <assert.h>
33156230Smux#include <zlib.h>
34156230Smux#include <err.h>
35156230Smux#include <errno.h>
36156230Smux#include <fcntl.h>
37156230Smux#include <stdarg.h>
38156230Smux#include <stdio.h>
39156230Smux#include <stdlib.h>
40156230Smux#include <string.h>
41156230Smux#include <unistd.h>
42156230Smux
43156230Smux#include "misc.h"
44156230Smux#include "stream.h"
45156230Smux
46156230Smux/*
47156230Smux * Simple stream API to make my life easier.  If the fgetln() and
48156230Smux * funopen() functions were standard and if funopen() wasn't using
49156230Smux * wrong types for the function pointers, I could have just used
50156230Smux * stdio, but life sucks.
51156230Smux *
52156230Smux * For now, streams are always block-buffered.
53156230Smux */
54156230Smux
55156230Smux/*
56156230Smux * Try to quiet warnings as much as possible with GCC while staying
57156230Smux * compatible with other compilers.
58156230Smux */
59156230Smux#ifndef __unused
60156230Smux#if defined(__GNUC__) && (__GNUC__ > 2 || __GNUC__ == 2 && __GNUC_MINOR__ >= 7)
61156230Smux#define	__unused	__attribute__((__unused__))
62156230Smux#else
63156230Smux#define	__unused
64156230Smux#endif
65156230Smux#endif
66156230Smux
67156230Smux/*
68156230Smux * Flags passed to the flush methods.
69156230Smux *
70156230Smux * STREAM_FLUSH_CLOSING is passed during the last flush call before
71156230Smux * closing a stream.  This allows the zlib filter to emit the EOF
72156230Smux * marker as appropriate.  In all other cases, STREAM_FLUSH_NORMAL
73156230Smux * should be passed.
74156230Smux *
75156230Smux * These flags are completely unused in the default flush method,
76156230Smux * but they are very important for the flush method of the zlib
77156230Smux * filter.
78156230Smux */
79156230Smuxtypedef enum {
80156230Smux	STREAM_FLUSH_NORMAL,
81156230Smux	STREAM_FLUSH_CLOSING
82156230Smux} stream_flush_t;
83156230Smux
84156230Smux/*
85156230Smux * This is because buf_new() will always allocate size + 1 bytes,
86156230Smux * so our buffer sizes will still be power of 2 values.
87156230Smux */
88156230Smux#define	STREAM_BUFSIZ	1023
89156230Smux
90156230Smuxstruct buf {
91156230Smux	char *buf;
92156230Smux	size_t size;
93156230Smux	size_t in;
94156230Smux	size_t off;
95156230Smux};
96156230Smux
97156230Smuxstruct stream {
98156230Smux	void *cookie;
99156230Smux	int fd;
100156230Smux	struct buf *rdbuf;
101156230Smux	struct buf *wrbuf;
102156230Smux	stream_readfn_t *readfn;
103156230Smux	stream_writefn_t *writefn;
104156230Smux	stream_closefn_t *closefn;
105156230Smux	int eof;
106156230Smux	struct stream_filter *filter;
107156230Smux	void *fdata;
108156230Smux};
109156230Smux
110156230Smuxtypedef int	stream_filter_initfn_t(struct stream *, void *);
111156230Smuxtypedef void	stream_filter_finifn_t(struct stream *);
112156230Smuxtypedef int	stream_filter_flushfn_t(struct stream *, struct buf *,
113156230Smux		    stream_flush_t);
114156230Smuxtypedef ssize_t	stream_filter_fillfn_t(struct stream *, struct buf *);
115156230Smux
116156230Smuxstruct stream_filter {
117156230Smux	stream_filter_t id;
118156230Smux	stream_filter_initfn_t *initfn;
119156230Smux	stream_filter_finifn_t *finifn;
120156230Smux	stream_filter_fillfn_t *fillfn;
121156230Smux	stream_filter_flushfn_t *flushfn;
122156230Smux};
123156230Smux
124156230Smux/* Low-level buffer API. */
125156230Smux#define	buf_avail(buf)		((buf)->size - (buf)->off - (buf)->in)
126156230Smux#define	buf_count(buf)		((buf)->in)
127156230Smux#define	buf_size(buf)		((buf)->size)
128156230Smux
129156230Smuxstatic struct buf	*buf_new(size_t);
130156230Smuxstatic void		 buf_more(struct buf *, size_t);
131156230Smuxstatic void		 buf_less(struct buf *, size_t);
132156230Smuxstatic void		 buf_free(struct buf *);
133156230Smuxstatic void		 buf_grow(struct buf *, size_t);
134156230Smux
135156230Smux/* Internal stream functions. */
136156230Smuxstatic ssize_t		 stream_fill(struct stream *);
137156230Smuxstatic ssize_t		 stream_fill_default(struct stream *, struct buf *);
138156230Smuxstatic int		 stream_flush_int(struct stream *, stream_flush_t);
139156230Smuxstatic int		 stream_flush_default(struct stream *, struct buf *,
140156230Smux			     stream_flush_t);
141156230Smux
142156230Smux/* Filters specific functions. */
143156230Smuxstatic struct stream_filter *stream_filter_lookup(stream_filter_t);
144156230Smuxstatic int		 stream_filter_init(struct stream *, void *);
145156230Smuxstatic void		 stream_filter_fini(struct stream *);
146156230Smux
147156230Smux/* The zlib stream filter declarations. */
148156230Smux#define	ZFILTER_EOF	1				/* Got Z_STREAM_END. */
149156230Smux
150156230Smuxstruct zfilter {
151156230Smux	int flags;
152156230Smux	struct buf *rdbuf;
153156230Smux	struct buf *wrbuf;
154156230Smux	z_stream *rdstate;
155156230Smux	z_stream *wrstate;
156156230Smux};
157156230Smux
158156230Smuxstatic int		 zfilter_init(struct stream *, void *);
159156230Smuxstatic void		 zfilter_fini(struct stream *);
160156230Smuxstatic ssize_t		 zfilter_fill(struct stream *, struct buf *);
161156230Smuxstatic int		 zfilter_flush(struct stream *, struct buf *,
162156230Smux			     stream_flush_t);
163156230Smux
164156230Smux/* The MD5 stream filter. */
165156230Smuxstruct md5filter {
166156230Smux	MD5_CTX ctx;
167156230Smux	char *md5;
168156230Smux};
169156230Smux
170156230Smuxstatic int		 md5filter_init(struct stream *, void *);
171156230Smuxstatic void		 md5filter_fini(struct stream *);
172156230Smuxstatic ssize_t		 md5filter_fill(struct stream *, struct buf *);
173156230Smuxstatic int		 md5filter_flush(struct stream *, struct buf *,
174156230Smux			     stream_flush_t);
175156230Smux
176156230Smux/* The available stream filters. */
177156230Smuxstruct stream_filter stream_filters[] = {
178156230Smux	{
179156230Smux		STREAM_FILTER_NULL,
180156230Smux		NULL,
181156230Smux		NULL,
182156230Smux		stream_fill_default,
183156230Smux		stream_flush_default
184156230Smux	},
185156230Smux	{
186156230Smux	       	STREAM_FILTER_ZLIB,
187156230Smux		zfilter_init,
188156230Smux		zfilter_fini,
189156230Smux		zfilter_fill,
190156230Smux		zfilter_flush
191156230Smux	},
192156230Smux	{
193156230Smux		STREAM_FILTER_MD5,
194156230Smux		md5filter_init,
195156230Smux		md5filter_fini,
196156230Smux		md5filter_fill,
197156230Smux		md5filter_flush
198156230Smux	}
199156230Smux};
200156230Smux
201156230Smux
202156230Smux/* Create a new buffer. */
203156230Smuxstatic struct buf *
204156230Smuxbuf_new(size_t size)
205156230Smux{
206156230Smux	struct buf *buf;
207156230Smux
208156230Smux	buf = xmalloc(sizeof(struct buf));
209156230Smux	/*
210156230Smux	 * We keep one spare byte so that stream_getln() can put a '\0'
211156230Smux	 * there in case the stream doesn't have an ending newline.
212156230Smux	 */
213156230Smux	buf->buf = xmalloc(size + 1);
214156230Smux	buf->size = size;
215156230Smux	buf->in = 0;
216156230Smux	buf->off = 0;
217156230Smux	return (buf);
218156230Smux}
219156230Smux
220156230Smux/*
221156230Smux * Grow the size of the buffer.  If "need" is 0, bump its size to the
222156230Smux * next power of 2 value.  Otherwise, bump it to the next power of 2
223156230Smux * value bigger than "need".
224156230Smux */
225156230Smuxstatic void
226156230Smuxbuf_grow(struct buf *buf, size_t need)
227156230Smux{
228156230Smux
229156230Smux	if (need == 0)
230156230Smux		buf->size = buf->size * 2 + 1; /* Account for the spare byte. */
231156230Smux	else {
232156230Smux		assert(need > buf->size);
233156230Smux		while (buf->size < need)
234156230Smux			buf->size = buf->size * 2 + 1;
235156230Smux	}
236156230Smux	buf->buf = xrealloc(buf->buf, buf->size + 1);
237156230Smux}
238156230Smux
239156230Smux/* Make more room in the buffer if needed. */
240156230Smuxstatic void
241156230Smuxbuf_prewrite(struct buf *buf)
242156230Smux{
243156230Smux
244156230Smux	if (buf_count(buf) == buf_size(buf))
245156230Smux		buf_grow(buf, 0);
246156230Smux	if (buf_count(buf) > 0 && buf_avail(buf) == 0) {
247156230Smux		memmove(buf->buf, buf->buf + buf->off, buf_count(buf));
248156230Smux		buf->off = 0;
249156230Smux	}
250156230Smux}
251156230Smux
252156230Smux/* Account for "n" bytes being added in the buffer. */
253156230Smuxstatic void
254156230Smuxbuf_more(struct buf *buf, size_t n)
255156230Smux{
256156230Smux
257156230Smux	assert(n <= buf_avail(buf));
258156230Smux	buf->in += n;
259156230Smux}
260156230Smux
261156230Smux/* Account for "n" bytes having been read in the buffer. */
262156230Smuxstatic void
263156230Smuxbuf_less(struct buf *buf, size_t n)
264156230Smux{
265156230Smux
266156230Smux	assert(n <= buf_count(buf));
267156230Smux	buf->in -= n;
268156230Smux	if (buf->in == 0)
269156230Smux		buf->off = 0;
270156230Smux	else
271156230Smux		buf->off += n;
272156230Smux}
273156230Smux
274156230Smux/* Free a buffer. */
275156230Smuxstatic void
276156230Smuxbuf_free(struct buf *buf)
277156230Smux{
278156230Smux
279156230Smux	free(buf->buf);
280156230Smux	free(buf);
281156230Smux}
282156230Smux
283156230Smuxstatic struct stream *
284156230Smuxstream_new(stream_readfn_t *readfn, stream_writefn_t *writefn,
285156230Smux    stream_closefn_t *closefn)
286156230Smux{
287156230Smux	struct stream *stream;
288156230Smux
289156230Smux	stream = xmalloc(sizeof(struct stream));
290156230Smux	if (readfn == NULL && writefn == NULL) {
291156230Smux		errno = EINVAL;
292156230Smux		return (NULL);
293156230Smux	}
294156230Smux	if (readfn != NULL)
295156230Smux		stream->rdbuf = buf_new(STREAM_BUFSIZ);
296156230Smux	else
297156230Smux		stream->rdbuf = NULL;
298156230Smux	if (writefn != NULL)
299156230Smux		stream->wrbuf = buf_new(STREAM_BUFSIZ);
300156230Smux	else
301156230Smux		stream->wrbuf = NULL;
302156230Smux	stream->cookie = NULL;
303156230Smux	stream->fd = -1;
304156230Smux	stream->readfn = readfn;
305156230Smux	stream->writefn = writefn;
306156230Smux	stream->closefn = closefn;
307156230Smux	stream->filter = stream_filter_lookup(STREAM_FILTER_NULL);
308156230Smux	stream->fdata = NULL;
309156230Smux	stream->eof = 0;
310156230Smux	return (stream);
311156230Smux}
312156230Smux
313156230Smux/* Create a new stream associated with a void *. */
314156230Smuxstruct stream *
315156230Smuxstream_open(void *cookie, stream_readfn_t *readfn, stream_writefn_t *writefn,
316156230Smux    stream_closefn_t *closefn)
317156230Smux{
318156230Smux	struct stream *stream;
319156230Smux
320156230Smux	stream = stream_new(readfn, writefn, closefn);
321156230Smux	stream->cookie = cookie;
322156230Smux	return (stream);
323156230Smux}
324156230Smux
325156230Smux/* Associate a file descriptor with a stream. */
326156230Smuxstruct stream *
327156230Smuxstream_open_fd(int fd, stream_readfn_t *readfn, stream_writefn_t *writefn,
328156230Smux    stream_closefn_t *closefn)
329156230Smux{
330156230Smux	struct stream *stream;
331156230Smux
332156230Smux	stream = stream_new(readfn, writefn, closefn);
333156230Smux	stream->cookie = &stream->fd;
334156230Smux	stream->fd = fd;
335156230Smux	return (stream);
336156230Smux}
337156230Smux
338156230Smux/* Like open() but returns a stream. */
339156230Smuxstruct stream *
340156230Smuxstream_open_file(const char *path, int flags, ...)
341156230Smux{
342156230Smux	struct stream *stream;
343156230Smux	stream_readfn_t *readfn;
344156230Smux	stream_writefn_t *writefn;
345156230Smux	va_list ap;
346156230Smux	mode_t mode;
347156230Smux	int fd;
348156230Smux
349156230Smux	va_start(ap, flags);
350156230Smux	if (flags & O_CREAT) {
351156230Smux		/*
352156230Smux		 * GCC says I should not be using mode_t here since it's
353156230Smux		 * promoted to an int when passed through `...'.
354156230Smux		 */
355156230Smux		mode = va_arg(ap, int);
356156230Smux		fd = open(path, flags, mode);
357156230Smux	} else
358156230Smux		fd = open(path, flags);
359156230Smux	va_end(ap);
360156230Smux	if (fd == -1)
361156230Smux		return (NULL);
362156230Smux
363156230Smux	flags &= O_ACCMODE;
364156230Smux	if (flags == O_RDONLY) {
365156230Smux		readfn = stream_read_fd;
366156230Smux		writefn = NULL;
367156230Smux	} else if (flags == O_WRONLY) {
368156230Smux		readfn = NULL;
369156230Smux		writefn = stream_write_fd;
370156230Smux	} else if (flags == O_RDWR) {
371156230Smux		assert(flags == O_RDWR);
372156230Smux		readfn = stream_read_fd;
373156230Smux		writefn = stream_write_fd;
374156230Smux	} else {
375156230Smux		errno = EINVAL;
376156230Smux		close(fd);
377156230Smux		return (NULL);
378156230Smux	}
379156230Smux
380156230Smux	stream = stream_open_fd(fd, readfn, writefn, stream_close_fd);
381156230Smux	if (stream == NULL)
382156230Smux		close(fd);
383156230Smux	return (stream);
384156230Smux}
385156230Smux
386156230Smux/* Return the file descriptor associated with this stream, or -1. */
387156230Smuxint
388156230Smuxstream_fileno(struct stream *stream)
389156230Smux{
390156230Smux
391156230Smux	return (stream->fd);
392156230Smux}
393156230Smux
394156230Smux/* Convenience read function for file descriptors. */
395156230Smuxssize_t
396156230Smuxstream_read_fd(void *cookie, void *buf, size_t size)
397156230Smux{
398156230Smux	ssize_t nbytes;
399156230Smux	int fd;
400156230Smux
401156230Smux	fd = *(int *)cookie;
402156230Smux	nbytes = read(fd, buf, size);
403156230Smux	return (nbytes);
404156230Smux}
405156230Smux
406156230Smux/* Convenience write function for file descriptors. */
407156230Smuxssize_t
408156230Smuxstream_write_fd(void *cookie, const void *buf, size_t size)
409156230Smux{
410156230Smux	ssize_t nbytes;
411156230Smux	int fd;
412156230Smux
413156230Smux	fd = *(int *)cookie;
414156230Smux	nbytes = write(fd, buf, size);
415156230Smux	return (nbytes);
416156230Smux}
417156230Smux
418156230Smux/* Convenience close function for file descriptors. */
419156230Smuxint
420156230Smuxstream_close_fd(void *cookie)
421156230Smux{
422156230Smux	int fd, ret;
423156230Smux
424156230Smux	fd = *(int *)cookie;
425156230Smux	ret = close(fd);
426156230Smux	return (ret);
427156230Smux}
428156230Smux
429156230Smux/* Read some bytes from the stream. */
430156230Smuxssize_t
431156230Smuxstream_read(struct stream *stream, void *buf, size_t size)
432156230Smux{
433156230Smux	struct buf *rdbuf;
434156230Smux	ssize_t ret;
435156230Smux	size_t n;
436156230Smux
437156230Smux	rdbuf = stream->rdbuf;
438156230Smux	if (buf_count(rdbuf) == 0) {
439156230Smux		ret = stream_fill(stream);
440156230Smux		if (ret <= 0)
441156230Smux			return (-1);
442156230Smux	}
443156230Smux	n = min(size, buf_count(rdbuf));
444156230Smux	memcpy(buf, rdbuf->buf + rdbuf->off, n);
445156230Smux	buf_less(rdbuf, n);
446156230Smux	return (n);
447156230Smux}
448156230Smux
449156230Smux/*
450156230Smux * Read a line from the stream and return a pointer to it.
451156230Smux *
452156230Smux * If "len" is non-NULL, the length of the string will be put into it.
453156230Smux * The pointer is only valid until the next stream API call.  The line
454156230Smux * can be modified by the caller, provided he doesn't write before or
455156230Smux * after it.
456156230Smux *
457156230Smux * This is somewhat similar to the BSD fgetln() function, except that
458156230Smux * "len" can be NULL here.  In that case the string is terminated by
459156230Smux * overwriting the '\n' character with a NUL character.  If it's the
460156230Smux * last line in the stream and it has no ending newline, we can still
461156230Smux * add '\0' after it, because we keep one spare byte in the buffers.
462156230Smux *
463156230Smux * However, be warned that one can't handle binary lines properly
464156230Smux * without knowing the size of the string since those can contain
465156230Smux * NUL characters.
466156230Smux */
467156230Smuxchar *
468156230Smuxstream_getln(struct stream *stream, size_t *len)
469156230Smux{
470156230Smux	struct buf *buf;
471156230Smux	char *cp, *line;
472156230Smux	ssize_t n;
473156230Smux	size_t done, size;
474156230Smux
475156230Smux	buf = stream->rdbuf;
476156230Smux	if (buf_count(buf) == 0) {
477156230Smux		n = stream_fill(stream);
478156230Smux		if (n <= 0)
479156230Smux			return (NULL);
480156230Smux	}
481156230Smux	cp = memchr(buf->buf + buf->off, '\n', buf_count(buf));
482156230Smux	for (done = buf_count(buf); cp == NULL; done += n) {
483156230Smux		n = stream_fill(stream);
484156230Smux		if (n < 0)
485156230Smux			return (NULL);
486156230Smux		if (n == 0)
487156230Smux			/* Last line of the stream. */
488156230Smux			cp = buf->buf + buf->off + buf->in - 1;
489156230Smux		else
490156230Smux			cp = memchr(buf->buf + buf->off + done, '\n',
491156230Smux			    buf_count(buf) - done);
492156230Smux	}
493156230Smux	line = buf->buf + buf->off;
494156230Smux	assert(cp >= line);
495156230Smux	size = cp - line + 1;
496156230Smux	buf_less(buf, size);
497156230Smux	if (len != NULL) {
498156230Smux		*len = size;
499156230Smux	} else {
500156230Smux		/* Terminate the string when len == NULL. */
501156230Smux		if (line[size - 1] == '\n')
502156230Smux			line[size - 1] = '\0';
503156230Smux		else
504156230Smux			line[size] = '\0';
505156230Smux	}
506156230Smux	return (line);
507156230Smux}
508156230Smux
509156230Smux/* Write some bytes to a stream. */
510156230Smuxssize_t
511156230Smuxstream_write(struct stream *stream, const void *src, size_t nbytes)
512156230Smux{
513156230Smux	struct buf *buf;
514156230Smux	int error;
515156230Smux
516156230Smux	buf = stream->wrbuf;
517156230Smux	if (nbytes > buf_size(buf))
518156230Smux		buf_grow(buf, nbytes);
519156230Smux	if (nbytes > buf_avail(buf)) {
520156230Smux		error = stream_flush_int(stream, STREAM_FLUSH_NORMAL);
521156230Smux		if (error)
522156230Smux			return (-1);
523156230Smux	}
524156230Smux	memcpy(buf->buf + buf->off + buf->in, src, nbytes);
525156230Smux	buf_more(buf, nbytes);
526156230Smux	return (nbytes);
527156230Smux}
528156230Smux
529156230Smux/* Formatted output to a stream. */
530156230Smuxint
531156230Smuxstream_printf(struct stream *stream, const char *fmt, ...)
532156230Smux{
533156230Smux	struct buf *buf;
534156230Smux	va_list ap;
535156230Smux	int error, ret;
536156230Smux
537156230Smux	buf = stream->wrbuf;
538156230Smuxagain:
539156230Smux	va_start(ap, fmt);
540156230Smux	ret = vsnprintf(buf->buf + buf->off + buf->in, buf_avail(buf), fmt, ap);
541156230Smux	va_end(ap);
542156230Smux	if (ret < 0)
543156230Smux		return (ret);
544156230Smux	if ((unsigned)ret >= buf_avail(buf)) {
545156230Smux		if ((unsigned)ret >= buf_size(buf))
546156230Smux			buf_grow(buf, ret + 1);
547156230Smux		if ((unsigned)ret >= buf_avail(buf)) {
548156230Smux			error = stream_flush_int(stream, STREAM_FLUSH_NORMAL);
549156230Smux			if (error)
550156230Smux				return (-1);
551156230Smux		}
552156230Smux		goto again;
553156230Smux	}
554156230Smux	buf_more(buf, ret);
555156230Smux	return (ret);
556156230Smux}
557156230Smux
558156230Smux/* Flush the entire write buffer of the stream. */
559156230Smuxint
560156230Smuxstream_flush(struct stream *stream)
561156230Smux{
562156230Smux	int error;
563156230Smux
564156230Smux	error = stream_flush_int(stream, STREAM_FLUSH_NORMAL);
565156230Smux	return (error);
566156230Smux}
567156230Smux
568156230Smux/* Internal flush API. */
569156230Smuxstatic int
570156230Smuxstream_flush_int(struct stream *stream, stream_flush_t how)
571156230Smux{
572156230Smux	struct buf *buf;
573156230Smux	int error;
574156230Smux
575156230Smux	buf = stream->wrbuf;
576156230Smux	error = (*stream->filter->flushfn)(stream, buf, how);
577156230Smux	assert(buf_count(buf) == 0);
578156230Smux	return (error);
579156230Smux}
580156230Smux
581156230Smux/* The default flush method. */
582156230Smuxstatic int
583156230Smuxstream_flush_default(struct stream *stream, struct buf *buf,
584156230Smux    stream_flush_t __unused how)
585156230Smux{
586156230Smux	ssize_t n;
587156230Smux
588156230Smux	while (buf_count(buf) > 0) {
589156230Smux		do {
590156230Smux			n = (*stream->writefn)(stream->cookie,
591156230Smux			    buf->buf + buf->off, buf_count(buf));
592156230Smux		} while (n == -1 && errno == EINTR);
593156230Smux		if (n <= 0)
594156230Smux			return (-1);
595156230Smux		buf_less(buf, n);
596156230Smux	}
597156230Smux	return (0);
598156230Smux}
599156230Smux
600156230Smux/* Flush the write buffer and call fsync() on the file descriptor. */
601156230Smuxint
602156230Smuxstream_sync(struct stream *stream)
603156230Smux{
604156230Smux	int error;
605156230Smux
606156230Smux	if (stream->fd == -1) {
607156230Smux		errno = EINVAL;
608156230Smux		return (-1);
609156230Smux	}
610156230Smux	error = stream_flush_int(stream, STREAM_FLUSH_NORMAL);
611156230Smux	if (error)
612156230Smux		return (-1);
613156230Smux	error = fsync(stream->fd);
614156230Smux	return (error);
615156230Smux}
616156230Smux
617156230Smux/* Like truncate() but on a stream. */
618156230Smuxint
619156230Smuxstream_truncate(struct stream *stream, off_t size)
620156230Smux{
621156230Smux	int error;
622156230Smux
623156230Smux	if (stream->fd == -1) {
624156230Smux		errno = EINVAL;
625156230Smux		return (-1);
626156230Smux	}
627156230Smux	error = stream_flush_int(stream, STREAM_FLUSH_NORMAL);
628156230Smux	if (error)
629156230Smux		return (-1);
630156230Smux	error = ftruncate(stream->fd, size);
631156230Smux	return (error);
632156230Smux}
633156230Smux
634156230Smux/* Like stream_truncate() except the off_t parameter is an offset. */
635156230Smuxint
636156230Smuxstream_truncate_rel(struct stream *stream, off_t off)
637156230Smux{
638156230Smux	struct stat sb;
639156230Smux	int error;
640156230Smux
641156230Smux	if (stream->fd == -1) {
642156230Smux		errno = EINVAL;
643156230Smux		return (-1);
644156230Smux	}
645156230Smux	error = stream_flush_int(stream, STREAM_FLUSH_NORMAL);
646156230Smux	if (error)
647156230Smux		return (-1);
648156230Smux	error = fstat(stream->fd, &sb);
649156230Smux	if (error)
650156230Smux		return (-1);
651156230Smux	error = stream_truncate(stream, sb.st_size + off);
652156230Smux	return (error);
653156230Smux}
654156230Smux
655156230Smux/* Rewind the stream. */
656156230Smuxint
657156230Smuxstream_rewind(struct stream *stream)
658156230Smux{
659156230Smux	int error;
660156230Smux
661156230Smux	if (stream->fd == -1) {
662156230Smux		errno = EINVAL;
663156230Smux		return (-1);
664156230Smux	}
665156230Smux	if (stream->rdbuf != NULL)
666156230Smux		buf_less(stream->rdbuf, buf_count(stream->rdbuf));
667156230Smux	if (stream->wrbuf != NULL) {
668156230Smux		error = stream_flush_int(stream, STREAM_FLUSH_NORMAL);
669156230Smux		if (error)
670156230Smux			return (error);
671156230Smux	}
672156230Smux	error = lseek(stream->fd, 0, SEEK_SET);
673156230Smux	return (error);
674156230Smux}
675156230Smux
676156230Smux/* Return EOF status. */
677156230Smuxint
678156230Smuxstream_eof(struct stream *stream)
679156230Smux{
680156230Smux
681156230Smux	return (stream->eof);
682156230Smux}
683156230Smux
684156230Smux/* Close a stream and free any resources held by it. */
685156230Smuxint
686156230Smuxstream_close(struct stream *stream)
687156230Smux{
688156230Smux	int error;
689156230Smux
690156230Smux	if (stream == NULL)
691156230Smux		return (0);
692156230Smux
693156230Smux	error = 0;
694156230Smux	if (stream->wrbuf != NULL)
695156230Smux		error = stream_flush_int(stream, STREAM_FLUSH_CLOSING);
696156230Smux	stream_filter_fini(stream);
697156230Smux	if (stream->closefn != NULL)
698156230Smux		/*
699156230Smux		 * We might overwrite a previous error from stream_flush(),
700156230Smux		 * but we have no choice, because wether it had worked or
701156230Smux		 * not, we need to close the file descriptor.
702156230Smux		 */
703156230Smux		error = (*stream->closefn)(stream->cookie);
704156230Smux	if (stream->rdbuf != NULL)
705156230Smux		buf_free(stream->rdbuf);
706156230Smux	if (stream->wrbuf != NULL)
707156230Smux		buf_free(stream->wrbuf);
708156230Smux	free(stream);
709156230Smux	return (error);
710156230Smux}
711156230Smux
712156230Smux/* The default fill method. */
713156230Smuxstatic ssize_t
714156230Smuxstream_fill_default(struct stream *stream, struct buf *buf)
715156230Smux{
716156230Smux	ssize_t n;
717156230Smux
718156230Smux	if (stream->eof)
719156230Smux		return (0);
720156230Smux	assert(buf_avail(buf) > 0);
721156230Smux	n = (*stream->readfn)(stream->cookie, buf->buf + buf->off + buf->in,
722156230Smux	    buf_avail(buf));
723156230Smux	if (n < 0)
724156230Smux		return (-1);
725156230Smux	if (n == 0) {
726156230Smux		stream->eof = 1;
727156230Smux		return (0);
728156230Smux	}
729156230Smux	buf_more(buf, n);
730156230Smux	return (n);
731156230Smux}
732156230Smux
733156230Smux/*
734156230Smux * Refill the read buffer.  This function is not permitted to return
735156230Smux * without having made more bytes available, unless there was an error.
736156230Smux * Moreover, stream_fill() returns the number of bytes added.
737156230Smux */
738156230Smuxstatic ssize_t
739156230Smuxstream_fill(struct stream *stream)
740156230Smux{
741156230Smux	struct stream_filter *filter;
742156230Smux	struct buf *buf;
743156230Smux#ifndef NDEBUG
744156230Smux	size_t oldcount;
745156230Smux#endif
746156230Smux	ssize_t n;
747156230Smux
748156230Smux	filter = stream->filter;
749156230Smux	buf = stream->rdbuf;
750156230Smux	buf_prewrite(buf);
751156230Smux#ifndef NDEBUG
752156230Smux	oldcount = buf_count(buf);
753156230Smux#endif
754156230Smux	n = (*filter->fillfn)(stream, buf);
755156230Smux	assert((n > 0 && n == (signed)(buf_count(buf) - oldcount)) ||
756156230Smux	    (n <= 0 && buf_count(buf) == oldcount));
757156230Smux	return (n);
758156230Smux}
759156230Smux
760156230Smux/*
761156230Smux * Lookup a stream filter.
762156230Smux *
763156230Smux * We are not supposed to get passed an invalid filter id, since
764156230Smux * filter ids are an enum type and we don't have invalid filter
765156230Smux * ids in the enum :-).  Thus, we are not checking for out of
766156230Smux * bounds access here.  If it happens, it's the caller's fault
767156230Smux * anyway.
768156230Smux */
769156230Smuxstatic struct stream_filter *
770156230Smuxstream_filter_lookup(stream_filter_t id)
771156230Smux{
772156230Smux	struct stream_filter *filter;
773156230Smux
774156230Smux	filter = stream_filters;
775156230Smux	while (filter->id != id)
776156230Smux		filter++;
777156230Smux	return (filter);
778156230Smux}
779156230Smux
780156230Smuxstatic int
781156230Smuxstream_filter_init(struct stream *stream, void *data)
782156230Smux{
783156230Smux	struct stream_filter *filter;
784156230Smux	int error;
785156230Smux
786156230Smux	filter = stream->filter;
787156230Smux	if (filter->initfn == NULL)
788156230Smux		return (0);
789156230Smux	error = (*filter->initfn)(stream, data);
790156230Smux	return (error);
791156230Smux}
792156230Smux
793156230Smuxstatic void
794156230Smuxstream_filter_fini(struct stream *stream)
795156230Smux{
796156230Smux	struct stream_filter *filter;
797156230Smux
798156230Smux	filter = stream->filter;
799156230Smux	if (filter->finifn != NULL)
800156230Smux		(*filter->finifn)(stream);
801156230Smux}
802156230Smux
803156230Smux/*
804156230Smux * Start a filter on a stream.
805156230Smux */
806156230Smuxint
807156230Smuxstream_filter_start(struct stream *stream, stream_filter_t id, void *data)
808156230Smux{
809156230Smux	struct stream_filter *filter;
810156230Smux	int error;
811156230Smux
812156230Smux	filter = stream->filter;
813156230Smux	if (id == filter->id)
814156230Smux		return (0);
815156230Smux	stream_filter_fini(stream);
816156230Smux	stream->filter = stream_filter_lookup(id);
817156230Smux	stream->fdata = NULL;
818156230Smux	error = stream_filter_init(stream, data);
819156230Smux	return (error);
820156230Smux}
821156230Smux
822156230Smux
823156230Smux/* Stop a filter, this is equivalent to setting the null filter. */
824156230Smuxvoid
825156230Smuxstream_filter_stop(struct stream *stream)
826156230Smux{
827156230Smux
828156230Smux	stream_filter_start(stream, STREAM_FILTER_NULL, NULL);
829156230Smux}
830156230Smux
831156230Smux/* The zlib stream filter implementation. */
832156230Smux
833156230Smux/* Take no chances with zlib... */
834156230Smuxstatic void *
835156230Smuxzfilter_alloc(void __unused *opaque, unsigned int items, unsigned int size)
836156230Smux{
837156230Smux
838156230Smux	return (xmalloc(items * size));
839156230Smux}
840156230Smux
841156230Smuxstatic void
842156230Smuxzfilter_free(void __unused *opaque, void *ptr)
843156230Smux{
844156230Smux
845156230Smux	free(ptr);
846156230Smux}
847156230Smux
848156230Smuxstatic int
849156230Smuxzfilter_init(struct stream *stream, void __unused *data)
850156230Smux{
851156230Smux	struct zfilter *zf;
852156230Smux	struct buf *buf;
853156230Smux	z_stream *state;
854156230Smux	int rv;
855156230Smux
856156230Smux	zf = xmalloc(sizeof(struct zfilter));
857156230Smux	memset(zf, 0, sizeof(struct zfilter));
858156230Smux	if (stream->rdbuf != NULL) {
859156230Smux		state = xmalloc(sizeof(z_stream));
860156230Smux		state->zalloc = zfilter_alloc;
861156230Smux		state->zfree = zfilter_free;
862156230Smux		state->opaque = Z_NULL;
863156230Smux		rv = inflateInit(state);
864156230Smux		if (rv != Z_OK)
865156230Smux			errx(1, "inflateInit: %s", state->msg);
866156230Smux		buf = buf_new(buf_size(stream->rdbuf));
867156230Smux		zf->rdbuf = stream->rdbuf;
868156230Smux		stream->rdbuf = buf;
869156230Smux		zf->rdstate = state;
870156230Smux	}
871156230Smux	if (stream->wrbuf != NULL) {
872156230Smux		state = xmalloc(sizeof(z_stream));
873156230Smux		state->zalloc = zfilter_alloc;
874156230Smux		state->zfree = zfilter_free;
875156230Smux		state->opaque = Z_NULL;
876156230Smux		rv = deflateInit(state, Z_DEFAULT_COMPRESSION);
877156230Smux		if (rv != Z_OK)
878156230Smux			errx(1, "deflateInit: %s", state->msg);
879156230Smux		buf = buf_new(buf_size(stream->wrbuf));
880156230Smux		zf->wrbuf = stream->wrbuf;
881156230Smux		stream->wrbuf = buf;
882156230Smux		zf->wrstate = state;
883156230Smux	}
884156230Smux	stream->fdata = zf;
885156230Smux	return (0);
886156230Smux}
887156230Smux
888156230Smuxstatic void
889156230Smuxzfilter_fini(struct stream *stream)
890156230Smux{
891156230Smux	struct zfilter *zf;
892156230Smux	struct buf *zbuf;
893156230Smux	z_stream *state;
894156230Smux	ssize_t n;
895156230Smux
896156230Smux	zf = stream->fdata;
897156230Smux	if (zf->rdbuf != NULL) {
898156230Smux		state = zf->rdstate;
899156230Smux		zbuf = zf->rdbuf;
900156230Smux		/*
901156230Smux		 * Even if it has produced all the bytes, zlib sometimes
902156230Smux		 * hasn't seen the EOF marker, so we need to call inflate()
903156230Smux		 * again to make sure we have eaten all the zlib'ed bytes.
904156230Smux		 */
905156230Smux		if ((zf->flags & ZFILTER_EOF) == 0) {
906156230Smux			n = zfilter_fill(stream, stream->rdbuf);
907156230Smux			assert(n == 0 && zf->flags & ZFILTER_EOF);
908156230Smux		}
909156230Smux		inflateEnd(state);
910156230Smux		free(state);
911156230Smux		buf_free(stream->rdbuf);
912156230Smux		stream->rdbuf = zbuf;
913156230Smux	}
914156230Smux	if (zf->wrbuf != NULL) {
915156230Smux		state = zf->wrstate;
916156230Smux		zbuf = zf->wrbuf;
917156230Smux		/*
918156230Smux		 * Compress the remaining bytes in the buffer, if any,
919156230Smux		 * and emit an EOF marker as appropriate.  We ignore
920156230Smux		 * the error because we can't do anything about it at
921156230Smux		 * this point, and it can happen if we're getting
922156230Smux		 * disconnected.
923156230Smux		 */
924156230Smux		(void)zfilter_flush(stream, stream->wrbuf,
925156230Smux		    STREAM_FLUSH_CLOSING);
926156230Smux		deflateEnd(state);
927156230Smux		free(state);
928156230Smux		buf_free(stream->wrbuf);
929156230Smux		stream->wrbuf = zbuf;
930156230Smux	}
931156230Smux	free(zf);
932156230Smux}
933156230Smux
934156230Smuxstatic int
935156230Smuxzfilter_flush(struct stream *stream, struct buf *buf, stream_flush_t how)
936156230Smux{
937156230Smux	struct zfilter *zf;
938156230Smux	struct buf *zbuf;
939156230Smux	z_stream *state;
940156230Smux	size_t lastin, lastout, ate, prod;
941156230Smux	int done, error, flags, rv;
942156230Smux
943156230Smux	zf = stream->fdata;
944156230Smux	state = zf->wrstate;
945156230Smux	zbuf = zf->wrbuf;
946156230Smux
947156230Smux	if (how == STREAM_FLUSH_NORMAL)
948156230Smux		flags = Z_SYNC_FLUSH;
949156230Smux	else
950156230Smux		flags = Z_FINISH;
951156230Smux
952156230Smux	done = 0;
953156230Smux	rv = Z_OK;
954156230Smux
955156230Smuxagain:
956156230Smux	/*
957156230Smux	 * According to zlib.h, we should have at least 6 bytes
958156230Smux	 * available when using deflate() with Z_SYNC_FLUSH.
959156230Smux	 */
960156230Smux	if ((buf_avail(zbuf) < 6 && flags == Z_SYNC_FLUSH) ||
961156230Smux	    rv == Z_BUF_ERROR || buf_avail(buf) == 0) {
962156230Smux		error = stream_flush_default(stream, zbuf, how);
963156230Smux		if (error)
964156230Smux			return (error);
965156230Smux	}
966156230Smux
967156230Smux	state->next_in = (Bytef *)(buf->buf + buf->off);
968156230Smux	state->avail_in = buf_count(buf);
969156230Smux	state->next_out = (Bytef *)(zbuf->buf + zbuf->off + zbuf->in);
970156230Smux	state->avail_out = buf_avail(zbuf);
971156230Smux	lastin = state->avail_in;
972156230Smux	lastout = state->avail_out;
973156230Smux	rv = deflate(state, flags);
974156230Smux	if (rv != Z_BUF_ERROR && rv != Z_OK && rv != Z_STREAM_END)
975156230Smux		errx(1, "deflate: %s", state->msg);
976156230Smux	ate = lastin - state->avail_in;
977156230Smux	prod = lastout - state->avail_out;
978156230Smux	buf_less(buf, ate);
979156230Smux	buf_more(zbuf, prod);
980156230Smux	if ((flags == Z_SYNC_FLUSH && buf_count(buf) > 0) ||
981156230Smux	    (flags == Z_FINISH && rv != Z_STREAM_END) ||
982156230Smux	    (rv == Z_BUF_ERROR))
983156230Smux		goto again;
984156230Smux
985156230Smux	assert(rv == Z_OK || (rv == Z_STREAM_END && flags == Z_FINISH));
986156230Smux	error = stream_flush_default(stream, zbuf, how);
987156230Smux	return (error);
988156230Smux}
989156230Smux
990156230Smuxstatic ssize_t
991156230Smuxzfilter_fill(struct stream *stream, struct buf *buf)
992156230Smux{
993156230Smux	struct zfilter *zf;
994156230Smux	struct buf *zbuf;
995156230Smux	z_stream *state;
996156230Smux	size_t lastin, lastout, new;
997156230Smux	ssize_t n;
998156230Smux	int rv;
999156230Smux
1000156230Smux	zf = stream->fdata;
1001156230Smux	state = zf->rdstate;
1002156230Smux	zbuf = zf->rdbuf;
1003156230Smux
1004156230Smux	assert(buf_avail(buf) > 0);
1005156230Smux	if (buf_count(zbuf) == 0) {
1006156230Smux		n = stream_fill_default(stream, zbuf);
1007156230Smux		if (n <= 0)
1008156230Smux			return (n);
1009156230Smux	}
1010156230Smuxagain:
1011156230Smux	assert(buf_count(zbuf) > 0);
1012156230Smux	state->next_in = (Bytef *)(zbuf->buf + zbuf->off);
1013156230Smux	state->avail_in = buf_count(zbuf);
1014156230Smux	state->next_out = (Bytef *)(buf->buf + buf->off + buf->in);
1015156230Smux	state->avail_out = buf_avail(buf);
1016156230Smux	lastin = state->avail_in;
1017156230Smux	lastout = state->avail_out;
1018156230Smux	rv = inflate(state, Z_SYNC_FLUSH);
1019156230Smux	buf_less(zbuf, lastin - state->avail_in);
1020156230Smux	new = lastout - state->avail_out;
1021156230Smux	if (new == 0 && rv != Z_STREAM_END) {
1022156230Smux		n = stream_fill_default(stream, zbuf);
1023156230Smux		if (n == -1)
1024156230Smux			return (-1);
1025156230Smux		if (n == 0)
1026156230Smux			return (0);
1027156230Smux		goto again;
1028156230Smux	}
1029156230Smux	if (rv != Z_STREAM_END && rv != Z_OK)
1030156230Smux		errx(1, "inflate: %s", state->msg);
1031156230Smux	if (rv == Z_STREAM_END)
1032156230Smux		zf->flags |= ZFILTER_EOF;
1033156230Smux	buf_more(buf, new);
1034156230Smux	return (new);
1035156230Smux}
1036156230Smux
1037156230Smux/* The MD5 stream filter implementation. */
1038156230Smuxstatic int
1039156230Smuxmd5filter_init(struct stream *stream, void *data)
1040156230Smux{
1041156230Smux	struct md5filter *mf;
1042156230Smux
1043156230Smux	mf = xmalloc(sizeof(struct md5filter));
1044156230Smux	MD5_Init(&mf->ctx);
1045156230Smux	mf->md5 = data;
1046156230Smux	stream->fdata = mf;
1047156230Smux	return (0);
1048156230Smux}
1049156230Smux
1050156230Smuxstatic void
1051156230Smuxmd5filter_fini(struct stream *stream)
1052156230Smux{
1053156230Smux	struct md5filter *mf;
1054156230Smux
1055156230Smux	mf = stream->fdata;
1056156230Smux	MD5_End(mf->md5, &mf->ctx);
1057156230Smux	free(stream->fdata);
1058156230Smux}
1059156230Smux
1060156230Smuxstatic ssize_t
1061156230Smuxmd5filter_fill(struct stream *stream, struct buf *buf)
1062156230Smux{
1063156230Smux	ssize_t n;
1064156230Smux
1065156230Smux	assert(buf_avail(buf) > 0);
1066156230Smux	n = stream_fill_default(stream, buf);
1067156230Smux	return (n);
1068156230Smux}
1069156230Smux
1070156230Smuxstatic int
1071156230Smuxmd5filter_flush(struct stream *stream, struct buf *buf, stream_flush_t how)
1072156230Smux{
1073156230Smux	struct md5filter *mf;
1074156230Smux	int error;
1075156230Smux
1076156230Smux	mf = stream->fdata;
1077156230Smux	MD5_Update(&mf->ctx, buf->buf + buf->off, buf->in);
1078156230Smux	error = stream_flush_default(stream, buf, how);
1079156230Smux	return (error);
1080156230Smux}
1081