1/*++
2/* NAME
3/*	vbuf 3
4/* SUMMARY
5/*	generic buffer package
6/* SYNOPSIS
7/*	#include <vbuf.h>
8/*
9/*	int	VBUF_GET(bp)
10/*	VBUF	*bp;
11/*
12/*	int	VBUF_PUT(bp, ch)
13/*	VBUF	*bp;
14/*	int	ch;
15/*
16/*	int	VBUF_SPACE(bp, len)
17/*	VBUF	*bp;
18/*	ssize_t	len;
19/*
20/*	int	vbuf_unget(bp, ch)
21/*	VBUF	*bp;
22/*	int	ch;
23/*
24/*	ssize_t	vbuf_read(bp, buf, len)
25/*	VBUF	*bp;
26/*	char	*buf;
27/*	ssize_t	len;
28/*
29/*	ssize_t	vbuf_write(bp, buf, len)
30/*	VBUF	*bp;
31/*	const char *buf;
32/*	ssize_t	len;
33/*
34/*	int	vbuf_err(bp)
35/*	VBUF	*bp;
36/*
37/*	int	vbuf_eof(bp)
38/*	VBUF	*bp;
39/*
40/*	int	vbuf_timeout(bp)
41/*	VBUF	*bp;
42/*
43/*	int	vbuf_clearerr(bp)
44/*	VBUF	*bp;
45/*
46/*	int	vbuf_rd_err(bp)
47/*	VBUF	*bp;
48/*
49/*	int	vbuf_wr_err(bp)
50/*	VBUF	*bp;
51/*
52/*	int	vbuf_rd_timeout(bp)
53/*	VBUF	*bp;
54/*
55/*	int	vbuf_wr_timeout(bp)
56/*	VBUF	*bp;
57/* DESCRIPTION
58/*	This module implements a buffer with read/write primitives that
59/*	automatically handle buffer-empty or buffer-full conditions.
60/*	The application is expected to provide callback routines that run
61/*	when the read-write primitives detect a buffer-empty/full condition.
62/*
63/*	VBUF buffers provide primitives to store and retrieve characters,
64/*	and to look up buffer status information.
65/*	By design, VBUF buffers provide no explicit primitives for buffer
66/*	memory management. This is left to the application to avoid any bias
67/*	toward specific management models. The application is free to use
68/*	whatever strategy suits best: memory-resident buffer, memory mapped
69/*	file, or stdio-like window to an open file.
70/*
71/*	VBUF_GET() returns the next character from the specified buffer,
72/*	or VBUF_EOF when none is available. VBUF_GET() is an unsafe macro
73/*	that evaluates its argument more than once.
74/*
75/*	VBUF_PUT() stores one character into the specified buffer. The result
76/*	is the stored character, or VBUF_EOF in case of problems. VBUF_PUT()
77/*	is an unsafe macro that evaluates its arguments more than once.
78/*
79/*	VBUF_SPACE() requests that the requested amount of buffer space be
80/*	made available, so that it can be accessed without using VBUF_PUT().
81/*	The result value is 0 for success, VBUF_EOF for problems.
82/*	VBUF_SPACE() is an unsafe macro that evaluates its arguments more
83/*	than once. VBUF_SPACE() does not support read-only streams.
84/*
85/*	vbuf_unget() provides at least one character of pushback, and returns
86/*	the pushed back character, or VBUF_EOF in case of problems. It is
87/*	an error to call vbuf_unget() on a buffer before reading any data
88/*	from it. vbuf_unget() clears the buffer's end-of-file indicator upon
89/*	success, and sets the buffer's error indicator when an attempt is
90/*	made to push back a non-character value.
91/*
92/*	vbuf_read() and vbuf_write() do bulk I/O. The result value is the
93/*	number of bytes transferred. A short count is returned in case of
94/*	an error.
95/*
96/*	vbuf_timeout() is a macro that returns non-zero if a timeout error
97/*	condition was detected while reading or writing the buffer. The
98/*	error status can be reset by calling vbuf_clearerr().
99/*
100/*	vbuf_err() is a macro that returns non-zero if a non-EOF error
101/*	(including timeout) condition was detected while reading or writing
102/*	the buffer. The error status can be reset by calling vbuf_clearerr().
103/*
104/*	The vbuf_rd_mumble() and vbuf_wr_mumble() macros report on
105/*	read and write error conditions, respectively.
106/*
107/*	vbuf_eof() is a macro that returns non-zero if an end-of-file
108/*	condition was detected while reading or writing the buffer. The error
109/*	status can be reset by calling vbuf_clearerr().
110/* APPLICATION CALLBACK SYNOPSIS
111/*	int	get_ready(bp)
112/*	VBUF	*bp;
113/*
114/*	int	put_ready(bp)
115/*	VBUF	*bp;
116/*
117/*	int	space(bp, len)
118/*	VBUF	*bp;
119/*	ssize_t	len;
120/* APPLICATION CALLBACK DESCRIPTION
121/* .ad
122/* .fi
123/*	get_ready() is called when VBUF_GET() detects a buffer-empty condition.
124/*	The result is zero when more data could be read, VBUF_EOF otherwise.
125/*
126/*	put_ready() is called when VBUF_PUT() detects a buffer-full condition.
127/*	The result is zero when the buffer could be flushed, VBUF_EOF otherwise.
128/*
129/*	space() performs whatever magic necessary to make at least \fIlen\fR
130/*	bytes available for access without using VBUF_PUT(). The result is 0
131/*	in case of success, VBUF_EOF otherwise.
132/* SEE ALSO
133/*	vbuf(3h) layout of the VBUF data structure.
134/* LICENSE
135/* .ad
136/* .fi
137/*	The Secure Mailer license must be distributed with this software.
138/* AUTHOR(S)
139/*	Wietse Venema
140/*	IBM T.J. Watson Research
141/*	P.O. Box 704
142/*	Yorktown Heights, NY 10598, USA
143/*--*/
144
145/* System library. */
146
147#include "sys_defs.h"
148#include <string.h>
149
150/* Utility library. */
151
152#include "vbuf.h"
153
154/* vbuf_unget - implement at least one character pushback */
155
156int     vbuf_unget(VBUF *bp, int ch)
157{
158    if ((ch & 0xff) != ch || -bp->cnt >= bp->len) {
159	bp->flags |= VBUF_FLAG_RD_ERR;		/* This error affects reads! */
160	return (VBUF_EOF);
161    } else {
162	bp->cnt--;
163	bp->flags &= ~VBUF_FLAG_EOF;
164	return (*--bp->ptr = ch);
165    }
166}
167
168/* vbuf_get - handle read buffer empty condition */
169
170int     vbuf_get(VBUF *bp)
171{
172    return (bp->get_ready(bp) ? VBUF_EOF : VBUF_GET(bp));
173}
174
175/* vbuf_put - handle write buffer full condition */
176
177int     vbuf_put(VBUF *bp, int ch)
178{
179    return (bp->put_ready(bp) ? VBUF_EOF : VBUF_PUT(bp, ch));
180}
181
182/* vbuf_read - bulk read from buffer */
183
184ssize_t vbuf_read(VBUF *bp, char *buf, ssize_t len)
185{
186    ssize_t count;
187    char   *cp;
188    ssize_t n;
189
190#if 0
191    for (count = 0; count < len; count++)
192	if ((buf[count] = VBUF_GET(bp)) < 0)
193	    break;
194    return (count);
195#else
196    for (cp = buf, count = len; count > 0; cp += n, count -= n) {
197	if (bp->cnt >= 0 && bp->get_ready(bp))
198	    break;
199	n = (count < -bp->cnt ? count : -bp->cnt);
200	memcpy(cp, bp->ptr, n);
201	bp->ptr += n;
202	bp->cnt += n;
203    }
204    return (len - count);
205#endif
206}
207
208/* vbuf_write - bulk write to buffer */
209
210ssize_t vbuf_write(VBUF *bp, const char *buf, ssize_t len)
211{
212    ssize_t count;
213    const char *cp;
214    ssize_t n;
215
216#if 0
217    for (count = 0; count < len; count++)
218	if (VBUF_PUT(bp, buf[count]) < 0)
219	    break;
220    return (count);
221#else
222    for (cp = buf, count = len; count > 0; cp += n, count -= n) {
223	if (bp->cnt <= 0 && bp->put_ready(bp) != 0)
224	    break;
225	n = (count < bp->cnt ? count : bp->cnt);
226	memcpy(bp->ptr, cp, n);
227	bp->ptr += n;
228	bp->cnt -= n;
229    }
230    return (len - count);
231#endif
232}
233