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

/* System library. */

#include "sys_defs.h"
#include <string.h>

/* Utility library. */

#include "vbuf.h"

/* vbuf_unget - implement at least one character pushback */

int     vbuf_unget(VBUF *bp, int ch)
{
    if ((ch & 0xff) != ch || -bp->cnt >= bp->len) {
	bp->flags |= VBUF_FLAG_RD_ERR;	/* This error affects reads! */
	return (VBUF_EOF);
    } else {
	bp->cnt--;
	bp->flags &= ~VBUF_FLAG_EOF;
	return (*--bp->ptr = ch);
    }
}

/* vbuf_get - handle read buffer empty condition */

int     vbuf_get(VBUF *bp)
{
    return (bp->get_ready(bp) ?
	((bp->flags |= VBUF_FLAG_EOF), VBUF_EOF) : VBUF_GET(bp));
}

/* vbuf_put - handle write buffer full condition */

int     vbuf_put(VBUF *bp, int ch)
{
    return (bp->put_ready(bp) ? VBUF_EOF : VBUF_PUT(bp, ch));
}

/* vbuf_read - bulk read from buffer */

ssize_t vbuf_read(VBUF *bp, void *buf, ssize_t len)
{
    ssize_t count;
    void   *cp;
    ssize_t n;

#if 0
    for (count = 0; count < len; count++)
	if ((buf[count] = VBUF_GET(bp)) < 0)
	    break;
    return (count);
#else
    for (cp = buf, count = len; count > 0; cp += n, count -= n) {
	if (bp->cnt >= 0 && bp->get_ready(bp))
	    break;
	n = (count < -bp->cnt ? count : -bp->cnt);
	memcpy(cp, bp->ptr, n);
	bp->ptr += n;
	bp->cnt += n;
    }
    return (len - count);
#endif
}

/* vbuf_write - bulk write to buffer */

ssize_t vbuf_write(VBUF *bp, const void *buf, ssize_t len)
{
    ssize_t count;
    const void *cp;
    ssize_t n;

#if 0
    for (count = 0; count < len; count++)
	if (VBUF_PUT(bp, buf[count]) < 0)
	    break;
    return (count);
#else
    for (cp = buf, count = len; count > 0; cp += n, count -= n) {
	if (bp->cnt <= 0 && bp->put_ready(bp) != 0)
	    break;
	n = (count < bp->cnt ? count : bp->cnt);
	memcpy(bp->ptr, cp, n);
	bp->ptr += n;
	bp->cnt -= n;
    }
    return (len - count);
#endif
}