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