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