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$
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;
100186781Slulf	int buf;
101156230Smux	struct buf *rdbuf;
102156230Smux	struct buf *wrbuf;
103156230Smux	stream_readfn_t *readfn;
104156230Smux	stream_writefn_t *writefn;
105156230Smux	stream_closefn_t *closefn;
106156230Smux	int eof;
107156230Smux	struct stream_filter *filter;
108156230Smux	void *fdata;
109156230Smux};
110156230Smux
111156230Smuxtypedef int	stream_filter_initfn_t(struct stream *, void *);
112156230Smuxtypedef void	stream_filter_finifn_t(struct stream *);
113156230Smuxtypedef int	stream_filter_flushfn_t(struct stream *, struct buf *,
114156230Smux		    stream_flush_t);
115156230Smuxtypedef ssize_t	stream_filter_fillfn_t(struct stream *, struct buf *);
116156230Smux
117156230Smuxstruct stream_filter {
118156230Smux	stream_filter_t id;
119156230Smux	stream_filter_initfn_t *initfn;
120156230Smux	stream_filter_finifn_t *finifn;
121156230Smux	stream_filter_fillfn_t *fillfn;
122156230Smux	stream_filter_flushfn_t *flushfn;
123156230Smux};
124156230Smux
125156230Smux/* Low-level buffer API. */
126156230Smux#define	buf_avail(buf)		((buf)->size - (buf)->off - (buf)->in)
127156230Smux#define	buf_count(buf)		((buf)->in)
128156230Smux#define	buf_size(buf)		((buf)->size)
129156230Smux
130156230Smuxstatic void		 buf_more(struct buf *, size_t);
131156230Smuxstatic void		 buf_less(struct buf *, size_t);
132156230Smuxstatic void		 buf_grow(struct buf *, size_t);
133156230Smux
134156230Smux/* Internal stream functions. */
135156230Smuxstatic ssize_t		 stream_fill(struct stream *);
136156230Smuxstatic ssize_t		 stream_fill_default(struct stream *, struct buf *);
137156230Smuxstatic int		 stream_flush_int(struct stream *, stream_flush_t);
138156230Smuxstatic int		 stream_flush_default(struct stream *, struct buf *,
139156230Smux			     stream_flush_t);
140156230Smux
141156230Smux/* Filters specific functions. */
142156230Smuxstatic struct stream_filter *stream_filter_lookup(stream_filter_t);
143156230Smuxstatic int		 stream_filter_init(struct stream *, void *);
144156230Smuxstatic void		 stream_filter_fini(struct stream *);
145156230Smux
146156230Smux/* The zlib stream filter declarations. */
147156230Smux#define	ZFILTER_EOF	1				/* Got Z_STREAM_END. */
148156230Smux
149156230Smuxstruct zfilter {
150156230Smux	int flags;
151156230Smux	struct buf *rdbuf;
152156230Smux	struct buf *wrbuf;
153156230Smux	z_stream *rdstate;
154156230Smux	z_stream *wrstate;
155156230Smux};
156156230Smux
157156230Smuxstatic int		 zfilter_init(struct stream *, void *);
158156230Smuxstatic void		 zfilter_fini(struct stream *);
159156230Smuxstatic ssize_t		 zfilter_fill(struct stream *, struct buf *);
160156230Smuxstatic int		 zfilter_flush(struct stream *, struct buf *,
161156230Smux			     stream_flush_t);
162156230Smux
163156230Smux/* The MD5 stream filter. */
164156230Smuxstruct md5filter {
165156230Smux	MD5_CTX ctx;
166156230Smux	char *md5;
167186781Slulf	char lastc;
168186781Slulf#define PRINT	1
169186781Slulf#define WS	2
170186781Slulf#define STRING	3
171186781Slulf#define SEEN	4
172186781Slulf	int state;
173156230Smux};
174156230Smux
175156230Smuxstatic int		 md5filter_init(struct stream *, void *);
176156230Smuxstatic void		 md5filter_fini(struct stream *);
177156230Smuxstatic ssize_t		 md5filter_fill(struct stream *, struct buf *);
178156230Smuxstatic int		 md5filter_flush(struct stream *, struct buf *,
179156230Smux			     stream_flush_t);
180186781Slulfstatic int		 md5rcsfilter_flush(struct stream *, struct buf *,
181186781Slulf			     stream_flush_t);
182156230Smux
183156230Smux/* The available stream filters. */
184156230Smuxstruct stream_filter stream_filters[] = {
185156230Smux	{
186156230Smux		STREAM_FILTER_NULL,
187156230Smux		NULL,
188156230Smux		NULL,
189156230Smux		stream_fill_default,
190156230Smux		stream_flush_default
191156230Smux	},
192156230Smux	{
193156230Smux	       	STREAM_FILTER_ZLIB,
194156230Smux		zfilter_init,
195156230Smux		zfilter_fini,
196156230Smux		zfilter_fill,
197156230Smux		zfilter_flush
198156230Smux	},
199156230Smux	{
200156230Smux		STREAM_FILTER_MD5,
201156230Smux		md5filter_init,
202156230Smux		md5filter_fini,
203156230Smux		md5filter_fill,
204156230Smux		md5filter_flush
205186781Slulf	},
206186781Slulf	{
207186781Slulf		STREAM_FILTER_MD5RCS,
208186781Slulf		md5filter_init,
209186781Slulf		md5filter_fini,
210186781Slulf		md5filter_fill,
211186781Slulf		md5rcsfilter_flush
212156230Smux	}
213186781Slulf
214156230Smux};
215156230Smux
216156230Smux
217156230Smux/* Create a new buffer. */
218186781Slulfstruct buf *
219156230Smuxbuf_new(size_t size)
220156230Smux{
221156230Smux	struct buf *buf;
222156230Smux
223156230Smux	buf = xmalloc(sizeof(struct buf));
224156230Smux	/*
225156230Smux	 * We keep one spare byte so that stream_getln() can put a '\0'
226156230Smux	 * there in case the stream doesn't have an ending newline.
227156230Smux	 */
228156230Smux	buf->buf = xmalloc(size + 1);
229186781Slulf	memset(buf->buf, 0, size + 1);
230156230Smux	buf->size = size;
231156230Smux	buf->in = 0;
232156230Smux	buf->off = 0;
233156230Smux	return (buf);
234156230Smux}
235156230Smux
236156230Smux/*
237156230Smux * Grow the size of the buffer.  If "need" is 0, bump its size to the
238156230Smux * next power of 2 value.  Otherwise, bump it to the next power of 2
239156230Smux * value bigger than "need".
240156230Smux */
241156230Smuxstatic void
242156230Smuxbuf_grow(struct buf *buf, size_t need)
243156230Smux{
244156230Smux
245156230Smux	if (need == 0)
246156230Smux		buf->size = buf->size * 2 + 1; /* Account for the spare byte. */
247156230Smux	else {
248156230Smux		assert(need > buf->size);
249156230Smux		while (buf->size < need)
250156230Smux			buf->size = buf->size * 2 + 1;
251156230Smux	}
252156230Smux	buf->buf = xrealloc(buf->buf, buf->size + 1);
253156230Smux}
254156230Smux
255156230Smux/* Make more room in the buffer if needed. */
256156230Smuxstatic void
257156230Smuxbuf_prewrite(struct buf *buf)
258156230Smux{
259156230Smux
260156230Smux	if (buf_count(buf) == buf_size(buf))
261156230Smux		buf_grow(buf, 0);
262156230Smux	if (buf_count(buf) > 0 && buf_avail(buf) == 0) {
263156230Smux		memmove(buf->buf, buf->buf + buf->off, buf_count(buf));
264156230Smux		buf->off = 0;
265156230Smux	}
266156230Smux}
267156230Smux
268156230Smux/* Account for "n" bytes being added in the buffer. */
269156230Smuxstatic void
270156230Smuxbuf_more(struct buf *buf, size_t n)
271156230Smux{
272156230Smux
273156230Smux	assert(n <= buf_avail(buf));
274156230Smux	buf->in += n;
275156230Smux}
276156230Smux
277156230Smux/* Account for "n" bytes having been read in the buffer. */
278156230Smuxstatic void
279156230Smuxbuf_less(struct buf *buf, size_t n)
280156230Smux{
281156230Smux
282156230Smux	assert(n <= buf_count(buf));
283156230Smux	buf->in -= n;
284156230Smux	if (buf->in == 0)
285156230Smux		buf->off = 0;
286156230Smux	else
287156230Smux		buf->off += n;
288156230Smux}
289156230Smux
290156230Smux/* Free a buffer. */
291186781Slulfvoid
292156230Smuxbuf_free(struct buf *buf)
293156230Smux{
294156230Smux
295156230Smux	free(buf->buf);
296156230Smux	free(buf);
297156230Smux}
298156230Smux
299156230Smuxstatic struct stream *
300156230Smuxstream_new(stream_readfn_t *readfn, stream_writefn_t *writefn,
301156230Smux    stream_closefn_t *closefn)
302156230Smux{
303156230Smux	struct stream *stream;
304156230Smux
305156230Smux	stream = xmalloc(sizeof(struct stream));
306156230Smux	if (readfn == NULL && writefn == NULL) {
307156230Smux		errno = EINVAL;
308156230Smux		return (NULL);
309156230Smux	}
310156230Smux	if (readfn != NULL)
311156230Smux		stream->rdbuf = buf_new(STREAM_BUFSIZ);
312156230Smux	else
313156230Smux		stream->rdbuf = NULL;
314156230Smux	if (writefn != NULL)
315156230Smux		stream->wrbuf = buf_new(STREAM_BUFSIZ);
316156230Smux	else
317156230Smux		stream->wrbuf = NULL;
318156230Smux	stream->cookie = NULL;
319156230Smux	stream->fd = -1;
320186781Slulf	stream->buf = 0;
321156230Smux	stream->readfn = readfn;
322156230Smux	stream->writefn = writefn;
323156230Smux	stream->closefn = closefn;
324156230Smux	stream->filter = stream_filter_lookup(STREAM_FILTER_NULL);
325156230Smux	stream->fdata = NULL;
326156230Smux	stream->eof = 0;
327156230Smux	return (stream);
328156230Smux}
329156230Smux
330156230Smux/* Create a new stream associated with a void *. */
331156230Smuxstruct stream *
332156230Smuxstream_open(void *cookie, stream_readfn_t *readfn, stream_writefn_t *writefn,
333156230Smux    stream_closefn_t *closefn)
334156230Smux{
335156230Smux	struct stream *stream;
336156230Smux
337156230Smux	stream = stream_new(readfn, writefn, closefn);
338156230Smux	stream->cookie = cookie;
339156230Smux	return (stream);
340156230Smux}
341156230Smux
342156230Smux/* Associate a file descriptor with a stream. */
343156230Smuxstruct stream *
344156230Smuxstream_open_fd(int fd, stream_readfn_t *readfn, stream_writefn_t *writefn,
345156230Smux    stream_closefn_t *closefn)
346156230Smux{
347156230Smux	struct stream *stream;
348156230Smux
349156230Smux	stream = stream_new(readfn, writefn, closefn);
350156230Smux	stream->cookie = &stream->fd;
351156230Smux	stream->fd = fd;
352156230Smux	return (stream);
353156230Smux}
354156230Smux
355186781Slulf/* Associate a buf with a stream. */
356186781Slulfstruct stream *
357186781Slulfstream_open_buf(struct buf *b)
358186781Slulf{
359186781Slulf	struct stream *stream;
360186781Slulf
361186781Slulf	stream = stream_new(stream_read_buf, stream_append_buf, stream_close_buf);
362186781Slulf	stream->cookie = b;
363186781Slulf	stream->buf = 1;
364186781Slulf	b->in = 0;
365186781Slulf	return (stream);
366186781Slulf}
367186781Slulf
368186781Slulf/*
369186781Slulf * Truncate a buffer, just decrease offset pointer.
370186781Slulf * XXX: this can be dangerous if not used correctly.
371186781Slulf */
372186781Slulfvoid
373186781Slulfstream_truncate_buf(struct buf *b, off_t off)
374186781Slulf{
375186781Slulf	b->off += off;
376186781Slulf}
377186781Slulf
378156230Smux/* Like open() but returns a stream. */
379156230Smuxstruct stream *
380156230Smuxstream_open_file(const char *path, int flags, ...)
381156230Smux{
382156230Smux	struct stream *stream;
383156230Smux	stream_readfn_t *readfn;
384156230Smux	stream_writefn_t *writefn;
385156230Smux	va_list ap;
386156230Smux	mode_t mode;
387156230Smux	int fd;
388156230Smux
389156230Smux	va_start(ap, flags);
390156230Smux	if (flags & O_CREAT) {
391156230Smux		/*
392156230Smux		 * GCC says I should not be using mode_t here since it's
393156230Smux		 * promoted to an int when passed through `...'.
394156230Smux		 */
395156230Smux		mode = va_arg(ap, int);
396156230Smux		fd = open(path, flags, mode);
397156230Smux	} else
398156230Smux		fd = open(path, flags);
399156230Smux	va_end(ap);
400156230Smux	if (fd == -1)
401156230Smux		return (NULL);
402156230Smux
403156230Smux	flags &= O_ACCMODE;
404156230Smux	if (flags == O_RDONLY) {
405156230Smux		readfn = stream_read_fd;
406156230Smux		writefn = NULL;
407156230Smux	} else if (flags == O_WRONLY) {
408156230Smux		readfn = NULL;
409156230Smux		writefn = stream_write_fd;
410156230Smux	} else if (flags == O_RDWR) {
411156230Smux		assert(flags == O_RDWR);
412156230Smux		readfn = stream_read_fd;
413156230Smux		writefn = stream_write_fd;
414156230Smux	} else {
415156230Smux		errno = EINVAL;
416156230Smux		close(fd);
417156230Smux		return (NULL);
418156230Smux	}
419156230Smux
420156230Smux	stream = stream_open_fd(fd, readfn, writefn, stream_close_fd);
421156230Smux	if (stream == NULL)
422156230Smux		close(fd);
423156230Smux	return (stream);
424156230Smux}
425156230Smux
426156230Smux/* Return the file descriptor associated with this stream, or -1. */
427156230Smuxint
428156230Smuxstream_fileno(struct stream *stream)
429156230Smux{
430156230Smux
431156230Smux	return (stream->fd);
432156230Smux}
433156230Smux
434186781Slulf/* Convenience read function for character buffers. */
435186781Slulfssize_t
436186781Slulfstream_read_buf(void *cookie, void *buf, size_t size)
437186781Slulf{
438186781Slulf	struct buf *b;
439186781Slulf	size_t avail;
440186781Slulf
441186781Slulf	/* Use in to be read offset. */
442186781Slulf	b = (struct buf *)cookie;
443186781Slulf	/* Just return what we have if the request is to large. */
444186781Slulf	avail = b->off - b->in;
445186781Slulf	if (avail < size) {
446186781Slulf		memcpy(buf, (b->buf + b->in), avail);
447186781Slulf		b->in += avail;
448186781Slulf		return (avail);
449186781Slulf	}
450186781Slulf	memcpy(buf, (b->buf + b->in), size);
451186781Slulf	b->in += size;
452186781Slulf	return (size);
453186781Slulf}
454186781Slulf
455186781Slulf/* Convenience write function for appending character buffers. */
456186781Slulfssize_t
457186781Slulfstream_append_buf(void *cookie, const void *buf, size_t size)
458186781Slulf{
459186781Slulf	struct buf *b;
460186781Slulf	size_t avail;
461186781Slulf
462186781Slulf	/* Use off to be write offset. */
463186781Slulf	b = (struct buf *)cookie;
464186781Slulf
465186781Slulf	avail = b->size - b->off;
466186781Slulf	if (size > avail)
467186781Slulf		buf_grow(b, b->size + size);
468186781Slulf	memcpy((b->buf + b->off), buf, size);
469186781Slulf	b->off += size;
470186781Slulf	b->buf[b->off] = '\0';
471186781Slulf	return (size);
472186781Slulf}
473186781Slulf
474186781Slulf/* Convenience close function for freeing character buffers. */
475186781Slulfint
476186781Slulfstream_close_buf(void *cookie)
477186781Slulf{
478186781Slulf	void *data;
479186781Slulf
480186781Slulf	data = cookie;
481186781Slulf	/* Basically a NOP. */
482186781Slulf	return (0);
483186781Slulf}
484186781Slulf
485156230Smux/* Convenience read function for file descriptors. */
486156230Smuxssize_t
487156230Smuxstream_read_fd(void *cookie, void *buf, size_t size)
488156230Smux{
489156230Smux	ssize_t nbytes;
490156230Smux	int fd;
491156230Smux
492156230Smux	fd = *(int *)cookie;
493156230Smux	nbytes = read(fd, buf, size);
494156230Smux	return (nbytes);
495156230Smux}
496156230Smux
497156230Smux/* Convenience write function for file descriptors. */
498156230Smuxssize_t
499156230Smuxstream_write_fd(void *cookie, const void *buf, size_t size)
500156230Smux{
501156230Smux	ssize_t nbytes;
502156230Smux	int fd;
503156230Smux
504156230Smux	fd = *(int *)cookie;
505156230Smux	nbytes = write(fd, buf, size);
506156230Smux	return (nbytes);
507156230Smux}
508156230Smux
509156230Smux/* Convenience close function for file descriptors. */
510156230Smuxint
511156230Smuxstream_close_fd(void *cookie)
512156230Smux{
513156230Smux	int fd, ret;
514156230Smux
515156230Smux	fd = *(int *)cookie;
516156230Smux	ret = close(fd);
517156230Smux	return (ret);
518156230Smux}
519156230Smux
520156230Smux/* Read some bytes from the stream. */
521156230Smuxssize_t
522156230Smuxstream_read(struct stream *stream, void *buf, size_t size)
523156230Smux{
524156230Smux	struct buf *rdbuf;
525156230Smux	ssize_t ret;
526156230Smux	size_t n;
527156230Smux
528156230Smux	rdbuf = stream->rdbuf;
529156230Smux	if (buf_count(rdbuf) == 0) {
530156230Smux		ret = stream_fill(stream);
531156230Smux		if (ret <= 0)
532156230Smux			return (-1);
533156230Smux	}
534156230Smux	n = min(size, buf_count(rdbuf));
535156230Smux	memcpy(buf, rdbuf->buf + rdbuf->off, n);
536156230Smux	buf_less(rdbuf, n);
537156230Smux	return (n);
538156230Smux}
539156230Smux
540186781Slulf/* A blocking stream_read call. */
541186781Slulfssize_t
542186781Slulfstream_read_blocking(struct stream *stream, void *buf, size_t size)
543186781Slulf{
544186781Slulf	struct buf *rdbuf;
545186781Slulf	ssize_t ret;
546186781Slulf	size_t n;
547186781Slulf
548186781Slulf	rdbuf = stream->rdbuf;
549186781Slulf	while (buf_count(rdbuf) <= size) {
550186781Slulf		ret = stream_fill(stream);
551186781Slulf		if (ret <= 0)
552186781Slulf			return (-1);
553186781Slulf	}
554186781Slulf	/* XXX: Should be at least size bytes in the buffer, right? */
555186781Slulf	/* Just do this to make sure. */
556186781Slulf	n = min(size, buf_count(rdbuf));
557186781Slulf	memcpy(buf, rdbuf->buf + rdbuf->off, n);
558186781Slulf	buf_less(rdbuf, n);
559186781Slulf	return (n);
560186781Slulf}
561186781Slulf
562156230Smux/*
563156230Smux * Read a line from the stream and return a pointer to it.
564156230Smux *
565156230Smux * If "len" is non-NULL, the length of the string will be put into it.
566156230Smux * The pointer is only valid until the next stream API call.  The line
567156230Smux * can be modified by the caller, provided he doesn't write before or
568156230Smux * after it.
569156230Smux *
570156230Smux * This is somewhat similar to the BSD fgetln() function, except that
571156230Smux * "len" can be NULL here.  In that case the string is terminated by
572156230Smux * overwriting the '\n' character with a NUL character.  If it's the
573156230Smux * last line in the stream and it has no ending newline, we can still
574156230Smux * add '\0' after it, because we keep one spare byte in the buffers.
575156230Smux *
576156230Smux * However, be warned that one can't handle binary lines properly
577156230Smux * without knowing the size of the string since those can contain
578156230Smux * NUL characters.
579156230Smux */
580156230Smuxchar *
581156230Smuxstream_getln(struct stream *stream, size_t *len)
582156230Smux{
583156230Smux	struct buf *buf;
584156230Smux	char *cp, *line;
585156230Smux	ssize_t n;
586156230Smux	size_t done, size;
587156230Smux
588156230Smux	buf = stream->rdbuf;
589156230Smux	if (buf_count(buf) == 0) {
590156230Smux		n = stream_fill(stream);
591156230Smux		if (n <= 0)
592156230Smux			return (NULL);
593156230Smux	}
594156230Smux	cp = memchr(buf->buf + buf->off, '\n', buf_count(buf));
595156230Smux	for (done = buf_count(buf); cp == NULL; done += n) {
596156230Smux		n = stream_fill(stream);
597156230Smux		if (n < 0)
598156230Smux			return (NULL);
599156230Smux		if (n == 0)
600156230Smux			/* Last line of the stream. */
601156230Smux			cp = buf->buf + buf->off + buf->in - 1;
602156230Smux		else
603156230Smux			cp = memchr(buf->buf + buf->off + done, '\n',
604156230Smux			    buf_count(buf) - done);
605156230Smux	}
606156230Smux	line = buf->buf + buf->off;
607156230Smux	assert(cp >= line);
608156230Smux	size = cp - line + 1;
609156230Smux	buf_less(buf, size);
610156230Smux	if (len != NULL) {
611156230Smux		*len = size;
612156230Smux	} else {
613156230Smux		/* Terminate the string when len == NULL. */
614156230Smux		if (line[size - 1] == '\n')
615156230Smux			line[size - 1] = '\0';
616156230Smux		else
617156230Smux			line[size] = '\0';
618156230Smux	}
619156230Smux	return (line);
620156230Smux}
621156230Smux
622156230Smux/* Write some bytes to a stream. */
623156230Smuxssize_t
624156230Smuxstream_write(struct stream *stream, const void *src, size_t nbytes)
625156230Smux{
626156230Smux	struct buf *buf;
627156230Smux	int error;
628156230Smux
629156230Smux	buf = stream->wrbuf;
630156230Smux	if (nbytes > buf_size(buf))
631156230Smux		buf_grow(buf, nbytes);
632156230Smux	if (nbytes > buf_avail(buf)) {
633156230Smux		error = stream_flush_int(stream, STREAM_FLUSH_NORMAL);
634156230Smux		if (error)
635156230Smux			return (-1);
636156230Smux	}
637156230Smux	memcpy(buf->buf + buf->off + buf->in, src, nbytes);
638156230Smux	buf_more(buf, nbytes);
639156230Smux	return (nbytes);
640156230Smux}
641156230Smux
642156230Smux/* Formatted output to a stream. */
643156230Smuxint
644156230Smuxstream_printf(struct stream *stream, const char *fmt, ...)
645156230Smux{
646156230Smux	struct buf *buf;
647156230Smux	va_list ap;
648156230Smux	int error, ret;
649156230Smux
650156230Smux	buf = stream->wrbuf;
651156230Smuxagain:
652156230Smux	va_start(ap, fmt);
653156230Smux	ret = vsnprintf(buf->buf + buf->off + buf->in, buf_avail(buf), fmt, ap);
654156230Smux	va_end(ap);
655156230Smux	if (ret < 0)
656156230Smux		return (ret);
657156230Smux	if ((unsigned)ret >= buf_avail(buf)) {
658156230Smux		if ((unsigned)ret >= buf_size(buf))
659156230Smux			buf_grow(buf, ret + 1);
660156230Smux		if ((unsigned)ret >= buf_avail(buf)) {
661156230Smux			error = stream_flush_int(stream, STREAM_FLUSH_NORMAL);
662156230Smux			if (error)
663156230Smux				return (-1);
664156230Smux		}
665156230Smux		goto again;
666156230Smux	}
667156230Smux	buf_more(buf, ret);
668156230Smux	return (ret);
669156230Smux}
670156230Smux
671156230Smux/* Flush the entire write buffer of the stream. */
672156230Smuxint
673156230Smuxstream_flush(struct stream *stream)
674156230Smux{
675156230Smux	int error;
676156230Smux
677156230Smux	error = stream_flush_int(stream, STREAM_FLUSH_NORMAL);
678156230Smux	return (error);
679156230Smux}
680156230Smux
681156230Smux/* Internal flush API. */
682156230Smuxstatic int
683156230Smuxstream_flush_int(struct stream *stream, stream_flush_t how)
684156230Smux{
685156230Smux	struct buf *buf;
686156230Smux	int error;
687156230Smux
688156230Smux	buf = stream->wrbuf;
689156230Smux	error = (*stream->filter->flushfn)(stream, buf, how);
690156230Smux	assert(buf_count(buf) == 0);
691156230Smux	return (error);
692156230Smux}
693156230Smux
694156230Smux/* The default flush method. */
695156230Smuxstatic int
696156230Smuxstream_flush_default(struct stream *stream, struct buf *buf,
697156230Smux    stream_flush_t __unused how)
698156230Smux{
699156230Smux	ssize_t n;
700156230Smux
701156230Smux	while (buf_count(buf) > 0) {
702156230Smux		do {
703156230Smux			n = (*stream->writefn)(stream->cookie,
704156230Smux			    buf->buf + buf->off, buf_count(buf));
705156230Smux		} while (n == -1 && errno == EINTR);
706156230Smux		if (n <= 0)
707156230Smux			return (-1);
708156230Smux		buf_less(buf, n);
709156230Smux	}
710156230Smux	return (0);
711156230Smux}
712156230Smux
713156230Smux/* Flush the write buffer and call fsync() on the file descriptor. */
714156230Smuxint
715156230Smuxstream_sync(struct stream *stream)
716156230Smux{
717156230Smux	int error;
718156230Smux
719156230Smux	if (stream->fd == -1) {
720156230Smux		errno = EINVAL;
721156230Smux		return (-1);
722156230Smux	}
723156230Smux	error = stream_flush_int(stream, STREAM_FLUSH_NORMAL);
724156230Smux	if (error)
725156230Smux		return (-1);
726156230Smux	error = fsync(stream->fd);
727156230Smux	return (error);
728156230Smux}
729156230Smux
730156230Smux/* Like truncate() but on a stream. */
731156230Smuxint
732156230Smuxstream_truncate(struct stream *stream, off_t size)
733156230Smux{
734156230Smux	int error;
735156230Smux
736156230Smux	if (stream->fd == -1) {
737156230Smux		errno = EINVAL;
738156230Smux		return (-1);
739156230Smux	}
740156230Smux	error = stream_flush_int(stream, STREAM_FLUSH_NORMAL);
741156230Smux	if (error)
742156230Smux		return (-1);
743156230Smux	error = ftruncate(stream->fd, size);
744156230Smux	return (error);
745156230Smux}
746156230Smux
747156230Smux/* Like stream_truncate() except the off_t parameter is an offset. */
748156230Smuxint
749156230Smuxstream_truncate_rel(struct stream *stream, off_t off)
750156230Smux{
751156230Smux	struct stat sb;
752156230Smux	int error;
753156230Smux
754186781Slulf	if (stream->buf) {
755186781Slulf		stream_truncate_buf(stream->cookie, off);
756186781Slulf		return (0);
757186781Slulf	}
758156230Smux	if (stream->fd == -1) {
759156230Smux		errno = EINVAL;
760156230Smux		return (-1);
761156230Smux	}
762156230Smux	error = stream_flush_int(stream, STREAM_FLUSH_NORMAL);
763156230Smux	if (error)
764156230Smux		return (-1);
765156230Smux	error = fstat(stream->fd, &sb);
766156230Smux	if (error)
767156230Smux		return (-1);
768156230Smux	error = stream_truncate(stream, sb.st_size + off);
769156230Smux	return (error);
770156230Smux}
771156230Smux
772156230Smux/* Rewind the stream. */
773156230Smuxint
774156230Smuxstream_rewind(struct stream *stream)
775156230Smux{
776156230Smux	int error;
777156230Smux
778156230Smux	if (stream->fd == -1) {
779156230Smux		errno = EINVAL;
780156230Smux		return (-1);
781156230Smux	}
782156230Smux	if (stream->rdbuf != NULL)
783156230Smux		buf_less(stream->rdbuf, buf_count(stream->rdbuf));
784156230Smux	if (stream->wrbuf != NULL) {
785156230Smux		error = stream_flush_int(stream, STREAM_FLUSH_NORMAL);
786156230Smux		if (error)
787156230Smux			return (error);
788156230Smux	}
789156230Smux	error = lseek(stream->fd, 0, SEEK_SET);
790156230Smux	return (error);
791156230Smux}
792156230Smux
793156230Smux/* Return EOF status. */
794156230Smuxint
795156230Smuxstream_eof(struct stream *stream)
796156230Smux{
797156230Smux
798156230Smux	return (stream->eof);
799156230Smux}
800156230Smux
801156230Smux/* Close a stream and free any resources held by it. */
802156230Smuxint
803156230Smuxstream_close(struct stream *stream)
804156230Smux{
805156230Smux	int error;
806156230Smux
807156230Smux	if (stream == NULL)
808156230Smux		return (0);
809156230Smux
810156230Smux	error = 0;
811156230Smux	if (stream->wrbuf != NULL)
812156230Smux		error = stream_flush_int(stream, STREAM_FLUSH_CLOSING);
813156230Smux	stream_filter_fini(stream);
814156230Smux	if (stream->closefn != NULL)
815156230Smux		/*
816156230Smux		 * We might overwrite a previous error from stream_flush(),
817156230Smux		 * but we have no choice, because wether it had worked or
818156230Smux		 * not, we need to close the file descriptor.
819156230Smux		 */
820156230Smux		error = (*stream->closefn)(stream->cookie);
821156230Smux	if (stream->rdbuf != NULL)
822156230Smux		buf_free(stream->rdbuf);
823156230Smux	if (stream->wrbuf != NULL)
824156230Smux		buf_free(stream->wrbuf);
825156230Smux	free(stream);
826156230Smux	return (error);
827156230Smux}
828156230Smux
829156230Smux/* The default fill method. */
830156230Smuxstatic ssize_t
831156230Smuxstream_fill_default(struct stream *stream, struct buf *buf)
832156230Smux{
833156230Smux	ssize_t n;
834156230Smux
835156230Smux	if (stream->eof)
836156230Smux		return (0);
837156230Smux	assert(buf_avail(buf) > 0);
838156230Smux	n = (*stream->readfn)(stream->cookie, buf->buf + buf->off + buf->in,
839156230Smux	    buf_avail(buf));
840156230Smux	if (n < 0)
841156230Smux		return (-1);
842156230Smux	if (n == 0) {
843156230Smux		stream->eof = 1;
844156230Smux		return (0);
845156230Smux	}
846156230Smux	buf_more(buf, n);
847156230Smux	return (n);
848156230Smux}
849156230Smux
850156230Smux/*
851156230Smux * Refill the read buffer.  This function is not permitted to return
852156230Smux * without having made more bytes available, unless there was an error.
853156230Smux * Moreover, stream_fill() returns the number of bytes added.
854156230Smux */
855156230Smuxstatic ssize_t
856156230Smuxstream_fill(struct stream *stream)
857156230Smux{
858156230Smux	struct stream_filter *filter;
859156230Smux	struct buf *buf;
860156230Smux#ifndef NDEBUG
861156230Smux	size_t oldcount;
862156230Smux#endif
863156230Smux	ssize_t n;
864156230Smux
865156230Smux	filter = stream->filter;
866156230Smux	buf = stream->rdbuf;
867156230Smux	buf_prewrite(buf);
868156230Smux#ifndef NDEBUG
869156230Smux	oldcount = buf_count(buf);
870156230Smux#endif
871156230Smux	n = (*filter->fillfn)(stream, buf);
872156230Smux	assert((n > 0 && n == (signed)(buf_count(buf) - oldcount)) ||
873156230Smux	    (n <= 0 && buf_count(buf) == oldcount));
874156230Smux	return (n);
875156230Smux}
876156230Smux
877156230Smux/*
878156230Smux * Lookup a stream filter.
879156230Smux *
880156230Smux * We are not supposed to get passed an invalid filter id, since
881156230Smux * filter ids are an enum type and we don't have invalid filter
882156230Smux * ids in the enum :-).  Thus, we are not checking for out of
883156230Smux * bounds access here.  If it happens, it's the caller's fault
884156230Smux * anyway.
885156230Smux */
886156230Smuxstatic struct stream_filter *
887156230Smuxstream_filter_lookup(stream_filter_t id)
888156230Smux{
889156230Smux	struct stream_filter *filter;
890156230Smux
891156230Smux	filter = stream_filters;
892156230Smux	while (filter->id != id)
893156230Smux		filter++;
894156230Smux	return (filter);
895156230Smux}
896156230Smux
897156230Smuxstatic int
898156230Smuxstream_filter_init(struct stream *stream, void *data)
899156230Smux{
900156230Smux	struct stream_filter *filter;
901156230Smux	int error;
902156230Smux
903156230Smux	filter = stream->filter;
904156230Smux	if (filter->initfn == NULL)
905156230Smux		return (0);
906156230Smux	error = (*filter->initfn)(stream, data);
907156230Smux	return (error);
908156230Smux}
909156230Smux
910156230Smuxstatic void
911156230Smuxstream_filter_fini(struct stream *stream)
912156230Smux{
913156230Smux	struct stream_filter *filter;
914156230Smux
915156230Smux	filter = stream->filter;
916156230Smux	if (filter->finifn != NULL)
917156230Smux		(*filter->finifn)(stream);
918156230Smux}
919156230Smux
920156230Smux/*
921156230Smux * Start a filter on a stream.
922156230Smux */
923156230Smuxint
924156230Smuxstream_filter_start(struct stream *stream, stream_filter_t id, void *data)
925156230Smux{
926156230Smux	struct stream_filter *filter;
927156230Smux	int error;
928156230Smux
929156230Smux	filter = stream->filter;
930156230Smux	if (id == filter->id)
931156230Smux		return (0);
932156230Smux	stream_filter_fini(stream);
933156230Smux	stream->filter = stream_filter_lookup(id);
934156230Smux	stream->fdata = NULL;
935156230Smux	error = stream_filter_init(stream, data);
936156230Smux	return (error);
937156230Smux}
938156230Smux
939156230Smux
940156230Smux/* Stop a filter, this is equivalent to setting the null filter. */
941156230Smuxvoid
942156230Smuxstream_filter_stop(struct stream *stream)
943156230Smux{
944156230Smux
945156230Smux	stream_filter_start(stream, STREAM_FILTER_NULL, NULL);
946156230Smux}
947156230Smux
948156230Smux/* The zlib stream filter implementation. */
949156230Smux
950156230Smux/* Take no chances with zlib... */
951156230Smuxstatic void *
952156230Smuxzfilter_alloc(void __unused *opaque, unsigned int items, unsigned int size)
953156230Smux{
954156230Smux
955156230Smux	return (xmalloc(items * size));
956156230Smux}
957156230Smux
958156230Smuxstatic void
959156230Smuxzfilter_free(void __unused *opaque, void *ptr)
960156230Smux{
961156230Smux
962156230Smux	free(ptr);
963156230Smux}
964156230Smux
965156230Smuxstatic int
966156230Smuxzfilter_init(struct stream *stream, void __unused *data)
967156230Smux{
968156230Smux	struct zfilter *zf;
969156230Smux	struct buf *buf;
970156230Smux	z_stream *state;
971156230Smux	int rv;
972156230Smux
973156230Smux	zf = xmalloc(sizeof(struct zfilter));
974156230Smux	memset(zf, 0, sizeof(struct zfilter));
975156230Smux	if (stream->rdbuf != NULL) {
976156230Smux		state = xmalloc(sizeof(z_stream));
977156230Smux		state->zalloc = zfilter_alloc;
978156230Smux		state->zfree = zfilter_free;
979156230Smux		state->opaque = Z_NULL;
980156230Smux		rv = inflateInit(state);
981156230Smux		if (rv != Z_OK)
982156230Smux			errx(1, "inflateInit: %s", state->msg);
983156230Smux		buf = buf_new(buf_size(stream->rdbuf));
984156230Smux		zf->rdbuf = stream->rdbuf;
985156230Smux		stream->rdbuf = buf;
986156230Smux		zf->rdstate = state;
987156230Smux	}
988156230Smux	if (stream->wrbuf != NULL) {
989156230Smux		state = xmalloc(sizeof(z_stream));
990156230Smux		state->zalloc = zfilter_alloc;
991156230Smux		state->zfree = zfilter_free;
992156230Smux		state->opaque = Z_NULL;
993156230Smux		rv = deflateInit(state, Z_DEFAULT_COMPRESSION);
994156230Smux		if (rv != Z_OK)
995156230Smux			errx(1, "deflateInit: %s", state->msg);
996156230Smux		buf = buf_new(buf_size(stream->wrbuf));
997156230Smux		zf->wrbuf = stream->wrbuf;
998156230Smux		stream->wrbuf = buf;
999156230Smux		zf->wrstate = state;
1000156230Smux	}
1001156230Smux	stream->fdata = zf;
1002156230Smux	return (0);
1003156230Smux}
1004156230Smux
1005156230Smuxstatic void
1006156230Smuxzfilter_fini(struct stream *stream)
1007156230Smux{
1008156230Smux	struct zfilter *zf;
1009156230Smux	struct buf *zbuf;
1010156230Smux	z_stream *state;
1011156230Smux	ssize_t n;
1012156230Smux
1013156230Smux	zf = stream->fdata;
1014156230Smux	if (zf->rdbuf != NULL) {
1015156230Smux		state = zf->rdstate;
1016156230Smux		zbuf = zf->rdbuf;
1017156230Smux		/*
1018156230Smux		 * Even if it has produced all the bytes, zlib sometimes
1019156230Smux		 * hasn't seen the EOF marker, so we need to call inflate()
1020156230Smux		 * again to make sure we have eaten all the zlib'ed bytes.
1021156230Smux		 */
1022156230Smux		if ((zf->flags & ZFILTER_EOF) == 0) {
1023156230Smux			n = zfilter_fill(stream, stream->rdbuf);
1024156230Smux			assert(n == 0 && zf->flags & ZFILTER_EOF);
1025156230Smux		}
1026156230Smux		inflateEnd(state);
1027156230Smux		free(state);
1028156230Smux		buf_free(stream->rdbuf);
1029156230Smux		stream->rdbuf = zbuf;
1030156230Smux	}
1031156230Smux	if (zf->wrbuf != NULL) {
1032156230Smux		state = zf->wrstate;
1033156230Smux		zbuf = zf->wrbuf;
1034156230Smux		/*
1035156230Smux		 * Compress the remaining bytes in the buffer, if any,
1036156230Smux		 * and emit an EOF marker as appropriate.  We ignore
1037156230Smux		 * the error because we can't do anything about it at
1038156230Smux		 * this point, and it can happen if we're getting
1039156230Smux		 * disconnected.
1040156230Smux		 */
1041156230Smux		(void)zfilter_flush(stream, stream->wrbuf,
1042156230Smux		    STREAM_FLUSH_CLOSING);
1043156230Smux		deflateEnd(state);
1044156230Smux		free(state);
1045156230Smux		buf_free(stream->wrbuf);
1046156230Smux		stream->wrbuf = zbuf;
1047156230Smux	}
1048156230Smux	free(zf);
1049156230Smux}
1050156230Smux
1051156230Smuxstatic int
1052156230Smuxzfilter_flush(struct stream *stream, struct buf *buf, stream_flush_t how)
1053156230Smux{
1054156230Smux	struct zfilter *zf;
1055156230Smux	struct buf *zbuf;
1056156230Smux	z_stream *state;
1057156230Smux	size_t lastin, lastout, ate, prod;
1058156230Smux	int done, error, flags, rv;
1059156230Smux
1060156230Smux	zf = stream->fdata;
1061156230Smux	state = zf->wrstate;
1062156230Smux	zbuf = zf->wrbuf;
1063156230Smux
1064156230Smux	if (how == STREAM_FLUSH_NORMAL)
1065156230Smux		flags = Z_SYNC_FLUSH;
1066156230Smux	else
1067156230Smux		flags = Z_FINISH;
1068156230Smux
1069156230Smux	done = 0;
1070156230Smux	rv = Z_OK;
1071156230Smux
1072156230Smuxagain:
1073156230Smux	/*
1074156230Smux	 * According to zlib.h, we should have at least 6 bytes
1075156230Smux	 * available when using deflate() with Z_SYNC_FLUSH.
1076156230Smux	 */
1077156230Smux	if ((buf_avail(zbuf) < 6 && flags == Z_SYNC_FLUSH) ||
1078156230Smux	    rv == Z_BUF_ERROR || buf_avail(buf) == 0) {
1079156230Smux		error = stream_flush_default(stream, zbuf, how);
1080156230Smux		if (error)
1081156230Smux			return (error);
1082156230Smux	}
1083156230Smux
1084156230Smux	state->next_in = (Bytef *)(buf->buf + buf->off);
1085156230Smux	state->avail_in = buf_count(buf);
1086156230Smux	state->next_out = (Bytef *)(zbuf->buf + zbuf->off + zbuf->in);
1087156230Smux	state->avail_out = buf_avail(zbuf);
1088156230Smux	lastin = state->avail_in;
1089156230Smux	lastout = state->avail_out;
1090156230Smux	rv = deflate(state, flags);
1091156230Smux	if (rv != Z_BUF_ERROR && rv != Z_OK && rv != Z_STREAM_END)
1092156230Smux		errx(1, "deflate: %s", state->msg);
1093156230Smux	ate = lastin - state->avail_in;
1094156230Smux	prod = lastout - state->avail_out;
1095156230Smux	buf_less(buf, ate);
1096156230Smux	buf_more(zbuf, prod);
1097156230Smux	if ((flags == Z_SYNC_FLUSH && buf_count(buf) > 0) ||
1098156230Smux	    (flags == Z_FINISH && rv != Z_STREAM_END) ||
1099156230Smux	    (rv == Z_BUF_ERROR))
1100156230Smux		goto again;
1101156230Smux
1102156230Smux	assert(rv == Z_OK || (rv == Z_STREAM_END && flags == Z_FINISH));
1103156230Smux	error = stream_flush_default(stream, zbuf, how);
1104156230Smux	return (error);
1105156230Smux}
1106156230Smux
1107156230Smuxstatic ssize_t
1108156230Smuxzfilter_fill(struct stream *stream, struct buf *buf)
1109156230Smux{
1110156230Smux	struct zfilter *zf;
1111156230Smux	struct buf *zbuf;
1112156230Smux	z_stream *state;
1113156230Smux	size_t lastin, lastout, new;
1114156230Smux	ssize_t n;
1115156230Smux	int rv;
1116156230Smux
1117156230Smux	zf = stream->fdata;
1118156230Smux	state = zf->rdstate;
1119156230Smux	zbuf = zf->rdbuf;
1120156230Smux
1121156230Smux	assert(buf_avail(buf) > 0);
1122156230Smux	if (buf_count(zbuf) == 0) {
1123156230Smux		n = stream_fill_default(stream, zbuf);
1124156230Smux		if (n <= 0)
1125156230Smux			return (n);
1126156230Smux	}
1127156230Smuxagain:
1128156230Smux	assert(buf_count(zbuf) > 0);
1129156230Smux	state->next_in = (Bytef *)(zbuf->buf + zbuf->off);
1130156230Smux	state->avail_in = buf_count(zbuf);
1131156230Smux	state->next_out = (Bytef *)(buf->buf + buf->off + buf->in);
1132156230Smux	state->avail_out = buf_avail(buf);
1133156230Smux	lastin = state->avail_in;
1134156230Smux	lastout = state->avail_out;
1135156230Smux	rv = inflate(state, Z_SYNC_FLUSH);
1136156230Smux	buf_less(zbuf, lastin - state->avail_in);
1137156230Smux	new = lastout - state->avail_out;
1138156230Smux	if (new == 0 && rv != Z_STREAM_END) {
1139156230Smux		n = stream_fill_default(stream, zbuf);
1140156230Smux		if (n == -1)
1141156230Smux			return (-1);
1142156230Smux		if (n == 0)
1143156230Smux			return (0);
1144156230Smux		goto again;
1145156230Smux	}
1146156230Smux	if (rv != Z_STREAM_END && rv != Z_OK)
1147156230Smux		errx(1, "inflate: %s", state->msg);
1148156230Smux	if (rv == Z_STREAM_END)
1149156230Smux		zf->flags |= ZFILTER_EOF;
1150156230Smux	buf_more(buf, new);
1151156230Smux	return (new);
1152156230Smux}
1153156230Smux
1154156230Smux/* The MD5 stream filter implementation. */
1155156230Smuxstatic int
1156156230Smuxmd5filter_init(struct stream *stream, void *data)
1157156230Smux{
1158156230Smux	struct md5filter *mf;
1159156230Smux
1160156230Smux	mf = xmalloc(sizeof(struct md5filter));
1161156230Smux	MD5_Init(&mf->ctx);
1162156230Smux	mf->md5 = data;
1163186781Slulf	mf->lastc = ';';
1164186781Slulf	mf->state = PRINT;
1165156230Smux	stream->fdata = mf;
1166156230Smux	return (0);
1167156230Smux}
1168156230Smux
1169156230Smuxstatic void
1170156230Smuxmd5filter_fini(struct stream *stream)
1171156230Smux{
1172156230Smux	struct md5filter *mf;
1173156230Smux
1174156230Smux	mf = stream->fdata;
1175156230Smux	MD5_End(mf->md5, &mf->ctx);
1176156230Smux	free(stream->fdata);
1177156230Smux}
1178156230Smux
1179156230Smuxstatic ssize_t
1180156230Smuxmd5filter_fill(struct stream *stream, struct buf *buf)
1181156230Smux{
1182156230Smux	ssize_t n;
1183156230Smux
1184156230Smux	assert(buf_avail(buf) > 0);
1185156230Smux	n = stream_fill_default(stream, buf);
1186156230Smux	return (n);
1187156230Smux}
1188156230Smux
1189156230Smuxstatic int
1190156230Smuxmd5filter_flush(struct stream *stream, struct buf *buf, stream_flush_t how)
1191156230Smux{
1192156230Smux	struct md5filter *mf;
1193156230Smux	int error;
1194156230Smux
1195156230Smux	mf = stream->fdata;
1196156230Smux	MD5_Update(&mf->ctx, buf->buf + buf->off, buf->in);
1197156230Smux	error = stream_flush_default(stream, buf, how);
1198156230Smux	return (error);
1199156230Smux}
1200186781Slulf
1201186781Slulf/* MD5 flush for RCS, where whitespaces are omitted. */
1202186781Slulfstatic int
1203186781Slulfmd5rcsfilter_flush(struct stream *stream, struct buf *buf, stream_flush_t how)
1204186781Slulf{
1205186781Slulf	struct md5filter *mf;
1206186781Slulf	char *ptr, *end;
1207186781Slulf	char *start;
1208186781Slulf	char space[2];
1209186781Slulf	int error;
1210186781Slulf
1211186781Slulf	mf = stream->fdata;
1212186781Slulf	space[0] = ' ';
1213186781Slulf	space[1] = '\0';
1214186781Slulf	ptr = buf->buf + buf->off;
1215186781Slulf	end = buf->buf + buf->off + buf->in;
1216186781Slulf
1217186781Slulf#define IS_WS(var) ((var) == ' ' || (var) == '\n' || (var) == '\t' || \
1218186781Slulf                    (var) == '\010' || (var) == '\013' || (var) == '\f' || \
1219186781Slulf                    (var) == '\r')
1220186781Slulf
1221186781Slulf#define IS_SPECIAL(var) ((var) == '$' || (var) == ',' || (var) == ':' || \
1222186781Slulf			 (var) == ';' || (var) == '@')
1223186781Slulf
1224186781Slulf#define IS_PRINT(var) (!IS_WS(var) && (var) != '@')
1225186781Slulf
1226186781Slulf	/* XXX: We can do better than this state machine. */
1227186781Slulf	while (ptr < end) {
1228186781Slulf		switch (mf->state) {
1229186781Slulf			/* Outside RCS statements. */
1230186781Slulf			case PRINT:
1231186781Slulf				start = ptr;
1232186781Slulf				while (ptr < end && IS_PRINT(*ptr)) {
1233186781Slulf					mf->lastc = *ptr;
1234186781Slulf					ptr++;
1235186781Slulf				}
1236186781Slulf				MD5_Update(&mf->ctx, start, (ptr - start));
1237186781Slulf				if (ptr < end) {
1238186781Slulf					if (*ptr == '@') {
1239186781Slulf						MD5_Update(&mf->ctx, ptr, 1);
1240186781Slulf						ptr++;
1241186781Slulf						mf->state = STRING;
1242186781Slulf					} else {
1243186781Slulf						mf->state = WS;
1244186781Slulf					}
1245186781Slulf				}
1246186781Slulf				break;
1247186781Slulf			case WS:
1248186781Slulf				while (ptr < end && IS_WS(*ptr)) {
1249186781Slulf					ptr++;
1250186781Slulf				}
1251186781Slulf				if (ptr < end) {
1252186781Slulf					if (*ptr == '@') {
1253186781Slulf						if (mf->lastc == '@') {
1254186781Slulf							MD5_Update(&mf->ctx,
1255186781Slulf							    space, 1);
1256186781Slulf						}
1257186781Slulf						MD5_Update(&mf->ctx, ptr, 1);
1258186781Slulf						ptr++;
1259186781Slulf						mf->state = STRING;
1260186781Slulf					} else {
1261186781Slulf						if (!IS_SPECIAL(*ptr) &&
1262186781Slulf						    !IS_SPECIAL(mf->lastc)) {
1263186781Slulf							MD5_Update(&mf->ctx,
1264186781Slulf							    space, 1);
1265186781Slulf						}
1266186781Slulf						mf->state = PRINT;
1267186781Slulf					}
1268186781Slulf				}
1269186781Slulf				break;
1270186781Slulf			case STRING:
1271186781Slulf				start = ptr;
1272186781Slulf				while (ptr < end && *ptr != '@') {
1273186781Slulf					ptr++;
1274186781Slulf				}
1275186781Slulf				MD5_Update(&mf->ctx, start, (ptr - start));
1276186781Slulf				if (ptr < end) {
1277186781Slulf					MD5_Update(&mf->ctx, ptr, 1);
1278186781Slulf					ptr++;
1279186781Slulf					mf->state = SEEN;
1280186781Slulf				}
1281186781Slulf				break;
1282186781Slulf			case SEEN:
1283186781Slulf				if (*ptr == '@') {
1284186781Slulf					MD5_Update(&mf->ctx, ptr, 1);
1285186781Slulf					ptr++;
1286186781Slulf					mf->state = STRING;
1287186781Slulf				} else if(IS_WS(*ptr)) {
1288186781Slulf					mf->lastc = '@';
1289186781Slulf					mf->state = WS;
1290186781Slulf				} else {
1291186781Slulf					mf->state = PRINT;
1292186781Slulf				}
1293186781Slulf				break;
1294186781Slulf			default:
1295186781Slulf				err(1, "Invalid state");
1296186781Slulf				break;
1297186781Slulf		}
1298186781Slulf	}
1299186781Slulf
1300186781Slulf	error = stream_flush_default(stream, buf, how);
1301186781Slulf	return (error);
1302186781Slulf}
1303186781Slulf
1304