xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/vstream.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: vstream.c,v 1.2 2017/02/14 01:16:49 christos Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	vstream 3
6 /* SUMMARY
7 /*	light-weight buffered I/O package
8 /* SYNOPSIS
9 /*	#include <vstream.h>
10 /*
11 /*	VSTREAM	*vstream_fopen(path, flags, mode)
12 /*	const char *path;
13 /*	int	flags;
14 /*	mode_t	mode;
15 /*
16 /*	VSTREAM	*vstream_fdopen(fd, flags)
17 /*	int	fd;
18 /*	int	flags;
19 /*
20 /*	int	vstream_fclose(stream)
21 /*	VSTREAM	*stream;
22 /*
23 /*	int	vstream_fdclose(stream)
24 /*	VSTREAM	*stream;
25 /*
26 /*	VSTREAM	*vstream_printf(format, ...)
27 /*	const char *format;
28 /*
29 /*	VSTREAM *vstream_fprintf(stream, format, ...)
30 /*	VSTREAM	*stream;
31 /*	const char *format;
32 /*
33 /*	int	VSTREAM_GETC(stream)
34 /*	VSTREAM	*stream;
35 /*
36 /*	int	VSTREAM_PUTC(ch, stream)
37 /*	int	ch;
38 /*
39 /*	int	VSTREAM_GETCHAR(void)
40 /*
41 /*	int	VSTREAM_PUTCHAR(ch)
42 /*	int	ch;
43 /*
44 /*	int	vstream_ungetc(stream, ch)
45 /*	VSTREAM	*stream;
46 /*	int	ch;
47 /*
48 /*	int	vstream_fputs(str, stream)
49 /*	const char *str;
50 /*	VSTREAM	*stream;
51 /*
52 /*	off_t	vstream_ftell(stream)
53 /*	VSTREAM	*stream;
54 /*
55 /*	off_t	vstream_fseek(stream, offset, whence)
56 /*	VSTREAM	*stream;
57 /*	off_t	offset;
58 /*	int	whence;
59 /*
60 /*	int	vstream_fflush(stream)
61 /*	VSTREAM	*stream;
62 /*
63 /*	int	vstream_fpurge(stream, direction)
64 /*	VSTREAM	*stream;
65 /*	int	direction;
66 /*
67 /*	ssize_t	vstream_fread(stream, buf, len)
68 /*	VSTREAM	*stream;
69 /*	void	*buf;
70 /*	ssize_t	len;
71 /*
72 /*	ssize_t	vstream_fwrite(stream, buf, len)
73 /*	VSTREAM	*stream;
74 /*	const void *buf;
75 /*	ssize_t	len;
76 /*
77 /*	void	vstream_control(stream, name, ...)
78 /*	VSTREAM	*stream;
79 /*	int	name;
80 /*
81 /*	int	vstream_fileno(stream)
82 /*	VSTREAM	*stream;
83 /*
84 /*	const ssize_t vstream_req_bufsize(stream)
85 /*	VSTREAM	*stream;
86 /*
87 /*	void	*vstream_context(stream)
88 /*	VSTREAM	*stream;
89 /*
90 /*	int	vstream_ferror(stream)
91 /*	VSTREAM	*stream;
92 /*
93 /*	int	vstream_ftimeout(stream)
94 /*	VSTREAM	*stream;
95 /*
96 /*	int	vstream_feof(stream)
97 /*	VSTREAM	*stream;
98 /*
99 /*	int	vstream_clearerr(stream)
100 /*	VSTREAM	*stream;
101 /*
102 /*	const char *VSTREAM_PATH(stream)
103 /*	VSTREAM	*stream;
104 /*
105 /*	char	*vstream_vprintf(format, ap)
106 /*	const char *format;
107 /*	va_list	*ap;
108 /*
109 /*	char	*vstream_vfprintf(stream, format, ap)
110 /*	VSTREAM	*stream;
111 /*	const char *format;
112 /*	va_list	*ap;
113 /*
114 /*	ssize_t	vstream_bufstat(stream, command)
115 /*	VSTREAM	*stream;
116 /*	int	command;
117 /*
118 /*	ssize_t	vstream_peek(stream)
119 /*	VSTREAM	*stream;
120 /*
121 /*	const char *vstream_peek_data(stream)
122 /*	VSTREAM	*stream;
123 /*
124 /*	int	vstream_setjmp(stream)
125 /*	VSTREAM	*stream;
126 /*
127 /*	void	vstream_longjmp(stream, val)
128 /*	VSTREAM	*stream;
129 /*	int	val;
130 /*
131 /*	time_t	vstream_ftime(stream)
132 /*	VSTREAM	*stream;
133 /*
134 /*	struct timeval vstream_ftimeval(stream)
135 /*	VSTREAM	*stream;
136 /*
137 /*	int	vstream_rd_error(stream)
138 /*	VSTREAM	*stream;
139 /*
140 /*	int	vstream_wr_error(stream)
141 /*	VSTREAM	*stream;
142 /*
143 /*	int	vstream_rd_timeout(stream)
144 /*	VSTREAM	*stream;
145 /*
146 /*	int	vstream_wr_timeout(stream)
147 /*	VSTREAM	*stream;
148 /*
149 /*	int	vstream_fstat(stream, flags)
150 /*	VSTREAM	*stream;
151 /*	int	flags;
152 /* DESCRIPTION
153 /*	The \fIvstream\fR module implements light-weight buffered I/O
154 /*	similar to the standard I/O routines.
155 /*
156 /*	The interface is implemented in terms of VSTREAM structure
157 /*	pointers, also called streams. For convenience, three streams
158 /*	are predefined: VSTREAM_IN, VSTREAM_OUT, and VSTREAM_ERR. These
159 /*	streams are connected to the standard input, output and error
160 /*	file descriptors, respectively.
161 /*
162 /*	Although the interface is patterned after the standard I/O
163 /*	library, there are some major differences:
164 /* .IP \(bu
165 /*	File descriptors are not limited to the range 0..255. This
166 /*	was reason #1 to write these routines in the first place.
167 /* .IP \(bu
168 /*	The application can switch between reading and writing on
169 /*	the same stream without having to perform a flush or seek
170 /*	operation, and can change write position without having to
171 /*	flush.  This was reason #2. Upon position or direction change,
172 /*	unread input is discarded, and unwritten output is flushed
173 /*	automatically. Exception: with double-buffered streams, unread
174 /*	input is not discarded upon change of I/O direction, and
175 /*	output flushing is delayed until the read buffer must be refilled.
176 /* .IP \(bu
177 /*	A bidirectional stream can read and write with the same buffer
178 /*	and file descriptor, or it can have separate read/write
179 /*	buffers and/or file descriptors.
180 /* .IP \(bu
181 /*	No automatic flushing of VSTREAM_OUT upon program exit, or of
182 /*	VSTREAM_ERR at any time. No unbuffered or line buffered modes.
183 /*	This functionality may be added when it is really needed.
184 /* .PP
185 /*	vstream_fopen() opens the named file and associates a buffered
186 /*	stream with it.  The \fIpath\fR, \fIflags\fR and \fImode\fR
187 /*	arguments are passed on to the open(2) routine. The result is
188 /*	a null pointer in case of problems. The \fIpath\fR argument is
189 /*	copied and can be looked up with VSTREAM_PATH().
190 /*
191 /*	vstream_fdopen() takes an open file and associates a buffered
192 /*	stream with it. The \fIflags\fR argument specifies how the file
193 /*	was opened. vstream_fdopen() either succeeds or never returns.
194 /*
195 /*	vstream_fclose() closes the named buffered stream. The result
196 /*	is 0 in case of success, VSTREAM_EOF in case of problems.
197 /*	vstream_fclose() reports the same errors as vstream_ferror().
198 /*
199 /*	vstream_fdclose() leaves the file(s) open but is otherwise
200 /*	identical to vstream_fclose().
201 /*
202 /*	vstream_fprintf() formats its arguments according to the
203 /*	\fIformat\fR argument and writes the result to the named stream.
204 /*	The result is the stream argument. It understands the s, c, d, u,
205 /*	o, x, X, e, f and g format types, the l modifier, field width and
206 /*	precision, sign, and padding with zeros or spaces. In addition,
207 /*	vstream_fprintf() recognizes the %m format specifier and expands
208 /*	it to the error message corresponding to the current value of the
209 /*	global \fIerrno\fR variable.
210 /*
211 /*	vstream_printf() performs formatted output to the standard output
212 /*	stream.
213 /*
214 /*	VSTREAM_GETC() reads the next character from the named stream.
215 /*	The result is VSTREAM_EOF when end-of-file is reached or if a read
216 /*	error was detected. VSTREAM_GETC() is an unsafe macro that
217 /*	evaluates some arguments more than once.
218 /*
219 /*	VSTREAM_GETCHAR() is an alias for VSTREAM_GETC(VSTREAM_IN).
220 /*
221 /*	VSTREAM_PUTC() appends the specified character to the specified
222 /*	stream. The result is the stored character, or VSTREAM_EOF in
223 /*	case of problems. VSTREAM_PUTC() is an unsafe macro that
224 /*	evaluates some arguments more than once.
225 /*
226 /*	VSTREAM_PUTCHAR(c) is an alias for VSTREAM_PUTC(c, VSTREAM_OUT).
227 /*
228 /*	vstream_ungetc() pushes back a character onto the specified stream
229 /*	and returns the character, or VSTREAM_EOF in case of problems.
230 /*	It is an error to push back before reading (or immediately after
231 /*	changing the stream offset via vstream_fseek()). Upon successful
232 /*	return, vstream_ungetc() clears the end-of-file stream flag.
233 /*
234 /*	vstream_fputs() appends the given null-terminated string to the
235 /*	specified buffered stream. The result is 0 in case of success,
236 /*	VSTREAM_EOF in case of problems.
237 /*
238 /*	vstream_ftell() returns the file offset for the specified stream,
239 /*	-1 if the stream is connected to a non-seekable file.
240 /*
241 /*	vstream_fseek() changes the file position for the next read or write
242 /*	operation. Unwritten output is flushed. With unidirectional streams,
243 /*	unread input is discarded. The \fIoffset\fR argument specifies the file
244 /*	position from the beginning of the file (\fIwhence\fR is SEEK_SET),
245 /*	from the current file position (\fIwhence\fR is SEEK_CUR), or from
246 /*	the file end (SEEK_END). The result value is the file offset
247 /*	from the beginning of the file, -1 in case of problems.
248 /*
249 /*	vstream_fflush() flushes unwritten data to a file that was
250 /*	opened in read-write or write-only mode.
251 /*	vstream_fflush() returns 0 in case of success, VSTREAM_EOF in
252 /*	case of problems. It is an error to flush a read-only stream.
253 /*	vstream_fflush() reports the same errors as vstream_ferror().
254 /*
255 /*	vstream_fpurge() discards the contents of the stream buffer.
256 /*	If direction is VSTREAM_PURGE_READ, it discards unread data,
257 /*	else if direction is VSTREAM_PURGE_WRITE, it discards unwritten
258 /*	data. In the case of a double-buffered stream, if direction is
259 /*	VSTREAM_PURGE_BOTH, it discards the content of both the read
260 /*	and write buffers. vstream_fpurge() returns 0 in case of success,
261 /*	VSTREAM_EOF in case of problems.
262 /*
263 /*	vstream_fread() and vstream_fwrite() perform unformatted I/O
264 /*	on the named stream. The result value is the number of bytes
265 /*	transferred. A short count is returned in case of end-of-file
266 /*	or error conditions.
267 /*
268 /*	vstream_control() allows the user to fine tune the behavior of
269 /*	the specified stream.  The arguments are a list of macros with
270 /*	zero or more arguments, terminated with CA_VSTREAM_CTL_END
271 /*	which has none.  The following lists the names and the types
272 /*	of the corresponding value arguments.
273 /* .IP "CA_VSTREAM_CTL_READ_FN(ssize_t (*)(int, void *, size_t, int, void *))"
274 /*	The argument specifies an alternative for the timed_read(3) function,
275 /*	for example, a read function that performs decryption.
276 /*	This function receives as arguments a file descriptor, buffer pointer,
277 /*	buffer length, timeout value, and the VSTREAM's context value.
278 /*	A timeout value <= 0 disables the time limit.
279 /*	This function should return the positive number of bytes transferred,
280 /*	0 upon EOF, and -1 upon error with errno set appropriately.
281 /* .IP "CA_VSTREAM_CTL_WRITE_FN(ssize_t (*)(int, void *, size_t, int, void *))"
282 /*	The argument specifies an alternative for the timed_write(3) function,
283 /*	for example, a write function that performs encryption.
284 /*	This function receives as arguments a file descriptor, buffer pointer,
285 /*	buffer length, timeout value, and the VSTREAM's context value.
286 /*	A timeout value <= 0 disables the time limit.
287 /*	This function should return the positive number of bytes transferred,
288 /*	and -1 upon error with errno set appropriately. Instead of -1 it may
289 /*	also return 0, e.g., upon remote party-initiated protocol shutdown.
290 /* .IP "CA_VSTREAM_CTL_CONTEXT(void *)"
291 /*	The argument specifies application context that is passed on to
292 /*	the application-specified read/write routines. No copy is made.
293 /* .IP "CA_VSTREAM_CTL_PATH(const char *)"
294 /*	Updates the stored pathname of the specified stream. The pathname
295 /*	is copied.
296 /* .IP "CA_VSTREAM_CTL_DOUBLE (no arguments)"
297 /*	Use separate buffers for reading and for writing.  This prevents
298 /*	unread input from being discarded upon change of I/O direction.
299 /* .IP "CA_VSTREAM_CTL_READ_FD(int)"
300 /*	The argument specifies the file descriptor to be used for reading.
301 /*	This feature is limited to double-buffered streams, and makes the
302 /*	stream non-seekable.
303 /* .IP "CA_VSTREAM_CTL_WRITE_FD(int)"
304 /*	The argument specifies the file descriptor to be used for writing.
305 /*	This feature is limited to double-buffered streams, and makes the
306 /*	stream non-seekable.
307 /* .IP "CA_VSTREAM_CTL_SWAP_FD(VSTREAM *)"
308 /*	The argument specifies a VSTREAM pointer; the request swaps the
309 /*	file descriptor members of the two streams. This feature is limited
310 /*	to streams that are both double-buffered or both single-buffered.
311 /* .IP "CA_VSTREAM_CTL_DUPFD(int)"
312 /*	The argument specifies a minimum file descriptor value. If
313 /*	the actual stream's file descriptors are below the minimum,
314 /*	reallocate the descriptors to the first free value greater
315 /*	than or equal to the minimum. The VSTREAM_CTL_DUPFD macro
316 /*	is defined only on systems with fcntl() F_DUPFD support.
317 /* .IP "CA_VSTREAM_CTL_WAITPID_FN(int (*)(pid_t, WAIT_STATUS_T *, int))"
318 /*	A pointer to function that behaves like waitpid(). This information
319 /*	is used by the vstream_pclose() routine.
320 /* .IP "CA_VSTREAM_CTL_TIMEOUT(int)"
321 /*	The deadline for a descriptor to become readable in case of a read
322 /*	request, or writable in case of a write request. Specify a value
323 /*	of 0 to disable deadlines.
324 /* .IP "CA_VSTREAM_CTL_EXCEPT (no arguments)"
325 /*	Enable exception handling with vstream_setjmp() and vstream_longjmp().
326 /*	This involves allocation of additional memory that normally isn't
327 /*	used.
328 /* .IP "CA_VSTREAM_CTL_BUFSIZE(ssize_t)"
329 /*	Specify a non-default buffer size for the next read(2) or
330 /*	write(2) operation, or zero to implement a no-op. Requests
331 /*	to reduce the buffer size are silently ignored (i.e. any
332 /*	positive value <= vstream_req_bufsize()). To get a buffer
333 /*	size smaller than VSTREAM_BUFSIZE, make the VSTREAM_CTL_BUFSIZE
334 /*	request before the first stream read or write operation
335 /*	(i.e., vstream_req_bufsize() returns zero).  Requests to
336 /*	change a fixed-size buffer (i.e., VSTREAM_ERR) are not
337 /*	allowed.
338 /*
339 /*	NOTE: the vstream_*printf() routines may silently expand a
340 /*	buffer, so that the result of some %letter specifiers can
341 /*	be written to contiguous memory.
342 /* .IP CA_VSTREAM_CTL_START_DEADLINE (no arguments)
343 /*	Change the VSTREAM_CTL_TIMEOUT behavior, to limit the total
344 /*	time for all subsequent file descriptor read or write
345 /*	operations, and recharge the deadline timer.
346 /* .IP CA_VSTREAM_CTL_STOP_DEADLINE (no arguments)
347 /*	Revert VSTREAM_CTL_TIMEOUT behavior to the default, i.e.
348 /*	a time limit for individual file descriptor read or write
349 /*	operations.
350 /* .PP
351 /*	vstream_fileno() gives access to the file handle associated with
352 /*	a buffered stream. With streams that have separate read/write
353 /*	file descriptors, the result is the current descriptor.
354 /*
355 /*	vstream_req_bufsize() returns the buffer size that will be
356 /*	used for the next read(2) or write(2) operation on the named
357 /*	stream. A zero result means that the next read(2) or write(2)
358 /*	operation will use the default buffer size (VSTREAM_BUFSIZE).
359 /*
360 /*	vstream_context() returns the application context that is passed on to
361 /*	the application-specified read/write routines.
362 /*
363 /*	VSTREAM_PATH() is an unsafe macro that returns the name stored
364 /*	with vstream_fopen() or with vstream_control(). The macro is
365 /*	unsafe because it evaluates some arguments more than once.
366 /*
367 /*	vstream_feof() returns non-zero when a previous operation on the
368 /*	specified stream caused an end-of-file condition.
369 /*	Although further read requests after EOF may complete
370 /*	succesfully, vstream_feof() will keep returning non-zero
371 /*	until vstream_clearerr() is called for that stream.
372 /*
373 /*	vstream_ferror() returns non-zero when a previous operation on the
374 /*	specified stream caused a non-EOF error condition, including timeout.
375 /*	After a non-EOF error on a stream, no I/O request will
376 /*	complete until after vstream_clearerr() is called for that stream.
377 /*
378 /*	vstream_ftimeout() returns non-zero when a previous operation on the
379 /*	specified stream caused a timeout error condition. See
380 /*	vstream_ferror() for error persistence details.
381 /*
382 /*	vstream_clearerr() resets the timeout, error and end-of-file indication
383 /*	of the specified stream, and returns no useful result.
384 /*
385 /*	vstream_vfprintf() provides an alternate interface
386 /*	for formatting an argument list according to a format string.
387 /*
388 /*	vstream_vprintf() provides a similar alternative interface.
389 /*
390 /*	vstream_bufstat() provides input and output buffer status
391 /*	information.  The command is one of the following:
392 /* .IP VSTREAM_BST_IN_PEND
393 /*	Return the number of characters that can be read without
394 /*	refilling the read buffer.
395 /* .IP VSTREAM_BST_OUT_PEND
396 /*	Return the number of characters that are waiting in the
397 /*	write buffer.
398 /* .PP
399 /*	vstream_peek() returns the number of characters that can be
400 /*	read from the named stream without refilling the read buffer.
401 /*	This is an alias for vstream_bufstat(stream, VSTREAM_BST_IN_PEND).
402 /*
403 /*	vstream_peek_data() returns a pointer to the unread bytes
404 /*	that exist according to vstream_peek(), or null if no unread
405 /*	bytes are available.
406 /*
407 /*	vstream_setjmp() saves processing context and makes that context
408 /*	available for use with vstream_longjmp().  Normally, vstream_setjmp()
409 /*	returns zero.  A non-zero result means that vstream_setjmp() returned
410 /*	through a vstream_longjmp() call; the result is the \fIval\fR argument
411 /*	given to vstream_longjmp().
412 /*
413 /*	NB: non-local jumps such as vstream_longjmp() are not safe
414 /*	for jumping out of any routine that manipulates VSTREAM data.
415 /*	longjmp() like calls are best avoided in signal handlers.
416 /*
417 /*	vstream_ftime() returns the time of initialization, the last buffer
418 /*	fill operation, or the last buffer flush operation for the specified
419 /*	stream. This information is maintained only when stream timeouts are
420 /*	enabled.
421 /*
422 /*	vstream_ftimeval() is like vstream_ftime() but returns more
423 /*	detail.
424 /*
425 /*	vstream_rd_mumble() and vstream_wr_mumble() report on
426 /*	read and write error conditions, respectively.
427 /*
428 /*	vstream_fstat() queries stream status information about
429 /*	user-requested features. The \fIflags\fR argument is the
430 /*	bitwise OR of one or more of the following, and the result
431 /*	value is the bitwise OR of the features that are activated.
432 /* .IP VSTREAM_FLAG_DEADLINE
433 /*	The deadline feature is activated.
434 /* .IP VSTREAM_FLAG_DOUBLE
435 /*	The double-buffering feature is activated.
436 /* DIAGNOSTICS
437 /*	Panics: interface violations. Fatal errors: out of memory.
438 /* SEE ALSO
439 /*	timed_read(3) default read routine
440 /*	timed_write(3) default write routine
441 /*	vbuf_print(3) formatting engine
442 /*	setjmp(3) non-local jumps
443 /* BUGS
444 /*	Should use mmap() on reasonable systems.
445 /* LICENSE
446 /* .ad
447 /* .fi
448 /*	The Secure Mailer license must be distributed with this software.
449 /* AUTHOR(S)
450 /*	Wietse Venema
451 /*	IBM T.J. Watson Research
452 /*	P.O. Box 704
453 /*	Yorktown Heights, NY 10598, USA
454 /*--*/
455 
456 /* System library. */
457 
458 #include <sys_defs.h>
459 #include <stdlib.h>			/* 44BSD stdarg.h uses abort() */
460 #include <stdarg.h>
461 #include <stddef.h>
462 #include <unistd.h>
463 #include <fcntl.h>
464 #include <time.h>
465 #include <errno.h>
466 #include <string.h>
467 
468 /* Utility library. */
469 
470 #include "mymalloc.h"
471 #include "msg.h"
472 #include "vbuf_print.h"
473 #include "iostuff.h"
474 #include "vstring.h"
475 #include "vstream.h"
476 
477 /* Application-specific. */
478 
479  /*
480   * Forward declarations.
481   */
482 static int vstream_buf_get_ready(VBUF *);
483 static int vstream_buf_put_ready(VBUF *);
484 static int vstream_buf_space(VBUF *, ssize_t);
485 
486  /*
487   * Initialization of the three pre-defined streams. Pre-allocate a static
488   * I/O buffer for the standard error stream, so that the error handler can
489   * produce a diagnostic even when memory allocation fails.
490   */
491 static unsigned char vstream_fstd_buf[VSTREAM_BUFSIZE];
492 
493 VSTREAM vstream_fstd[] = {
494     {{
495 	    0,				/* flags */
496 	    0, 0, 0, 0,			/* buffer */
497 	    vstream_buf_get_ready, vstream_buf_put_ready, vstream_buf_space,
498     }, STDIN_FILENO, (VSTREAM_RW_FN) timed_read, (VSTREAM_RW_FN) timed_write,
499     0,},
500     {{
501 	    0,				/* flags */
502 	    0, 0, 0, 0,			/* buffer */
503 	    vstream_buf_get_ready, vstream_buf_put_ready, vstream_buf_space,
504     }, STDOUT_FILENO, (VSTREAM_RW_FN) timed_read, (VSTREAM_RW_FN) timed_write,
505     0,},
506     {{
507 	    VBUF_FLAG_FIXED | VSTREAM_FLAG_WRITE,
508 	    vstream_fstd_buf, VSTREAM_BUFSIZE, VSTREAM_BUFSIZE, vstream_fstd_buf,
509 	    vstream_buf_get_ready, vstream_buf_put_ready, vstream_buf_space,
510     }, STDERR_FILENO, (VSTREAM_RW_FN) timed_read, (VSTREAM_RW_FN) timed_write,
511     VSTREAM_BUFSIZE,},
512 };
513 
514 #define VSTREAM_STATIC(v) ((v) >= VSTREAM_IN && (v) <= VSTREAM_ERR)
515 
516  /*
517   * A bunch of macros to make some expressions more readable. XXX We're
518   * assuming that O_RDONLY == 0, O_WRONLY == 1, O_RDWR == 2.
519   */
520 #define VSTREAM_ACC_MASK(f)	((f) & (O_APPEND | O_WRONLY | O_RDWR))
521 
522 #define VSTREAM_CAN_READ(f)	(VSTREAM_ACC_MASK(f) == O_RDONLY \
523 				|| VSTREAM_ACC_MASK(f) == O_RDWR)
524 #define VSTREAM_CAN_WRITE(f)	(VSTREAM_ACC_MASK(f) & O_WRONLY \
525 				|| VSTREAM_ACC_MASK(f) & O_RDWR \
526 				|| VSTREAM_ACC_MASK(f) & O_APPEND)
527 
528 #define VSTREAM_BUF_COUNT(bp, n) \
529 	((bp)->flags & VSTREAM_FLAG_READ ? -(n) : (n))
530 
531 #define VSTREAM_BUF_AT_START(bp) { \
532 	(bp)->cnt = VSTREAM_BUF_COUNT((bp), (bp)->len); \
533 	(bp)->ptr = (bp)->data; \
534     }
535 
536 #define VSTREAM_BUF_AT_OFFSET(bp, offset) { \
537 	(bp)->ptr = (bp)->data + (offset); \
538 	(bp)->cnt = VSTREAM_BUF_COUNT(bp, (bp)->len - (offset)); \
539     }
540 
541 #define VSTREAM_BUF_AT_END(bp) { \
542 	(bp)->cnt = 0; \
543 	(bp)->ptr = (bp)->data + (bp)->len; \
544     }
545 
546 #define VSTREAM_BUF_ZERO(bp) { \
547 	(bp)->flags = 0; \
548 	(bp)->data = (bp)->ptr = 0; \
549 	(bp)->len = (bp)->cnt = 0; \
550     }
551 
552 #define VSTREAM_BUF_ACTIONS(bp, get_action, put_action, space_action) { \
553 	(bp)->get_ready = (get_action); \
554 	(bp)->put_ready = (put_action); \
555 	(bp)->space = (space_action); \
556     }
557 
558 #define VSTREAM_SAVE_STATE(stream, buffer, filedes) { \
559 	stream->buffer = stream->buf; \
560 	stream->filedes = stream->fd; \
561     }
562 
563 #define VSTREAM_RESTORE_STATE(stream, buffer, filedes) do { \
564 	stream->buffer.flags = stream->buf.flags; \
565 	stream->buf = stream->buffer; \
566 	stream->fd = stream->filedes; \
567     } while(0)
568 
569 #define VSTREAM_FORK_STATE(stream, buffer, filedes) { \
570 	stream->buffer = stream->buf; \
571 	stream->filedes = stream->fd; \
572 	stream->buffer.data = stream->buffer.ptr = 0; \
573 	stream->buffer.len = stream->buffer.cnt = 0; \
574 	stream->buffer.flags &= ~VSTREAM_FLAG_FIXED; \
575     };
576 
577 #define VSTREAM_FLAG_READ_DOUBLE (VSTREAM_FLAG_READ | VSTREAM_FLAG_DOUBLE)
578 #define VSTREAM_FLAG_WRITE_DOUBLE (VSTREAM_FLAG_WRITE | VSTREAM_FLAG_DOUBLE)
579 
580 #define VSTREAM_FFLUSH_SOME(stream) \
581 	vstream_fflush_some((stream), (stream)->buf.len - (stream)->buf.cnt)
582 
583 /* Note: this does not change a negative result into a zero result. */
584 #define VSTREAM_SUB_TIME(x, y, z) \
585     do { \
586 	(x).tv_sec = (y).tv_sec - (z).tv_sec; \
587 	(x).tv_usec = (y).tv_usec - (z).tv_usec; \
588 	while ((x).tv_usec < 0) { \
589 	    (x).tv_usec += 1000000; \
590 	    (x).tv_sec -= 1; \
591 	} \
592 	while ((x).tv_usec >= 1000000) { \
593 	    (x).tv_usec -= 1000000; \
594 	    (x).tv_sec += 1; \
595 	} \
596     } while (0)
597 
598 /* vstream_buf_init - initialize buffer */
599 
600 static void vstream_buf_init(VBUF *bp, int flags)
601 {
602 
603     /*
604      * Initialize the buffer such that the first data access triggers a
605      * buffer boundary action.
606      */
607     VSTREAM_BUF_ZERO(bp);
608     VSTREAM_BUF_ACTIONS(bp,
609 			VSTREAM_CAN_READ(flags) ? vstream_buf_get_ready : 0,
610 			VSTREAM_CAN_WRITE(flags) ? vstream_buf_put_ready : 0,
611 			vstream_buf_space);
612 }
613 
614 /* vstream_buf_alloc - allocate buffer memory */
615 
616 static void vstream_buf_alloc(VBUF *bp, ssize_t len)
617 {
618     VSTREAM *stream = VBUF_TO_APPL(bp, VSTREAM, buf);
619     ssize_t used = bp->ptr - bp->data;
620     const char *myname = "vstream_buf_alloc";
621 
622     if (len < bp->len)
623 	msg_panic("%s: attempt to shrink buffer", myname);
624     if (bp->flags & VSTREAM_FLAG_FIXED)
625 	msg_panic("%s: unable to extend fixed-size buffer", myname);
626 
627     /*
628      * Late buffer allocation allows the user to override the default policy.
629      * If a buffer already exists, allow for the presence of (output) data.
630      */
631     bp->data = (unsigned char *)
632 	(bp->data ? myrealloc((void *) bp->data, len) : mymalloc(len));
633     bp->len = len;
634     if (bp->flags & VSTREAM_FLAG_READ) {
635 	bp->ptr = bp->data + used;
636 	if (bp->flags & VSTREAM_FLAG_DOUBLE)
637 	    VSTREAM_SAVE_STATE(stream, read_buf, read_fd);
638     } else {
639 	VSTREAM_BUF_AT_OFFSET(bp, used);
640 	if (bp->flags & VSTREAM_FLAG_DOUBLE)
641 	    VSTREAM_SAVE_STATE(stream, write_buf, write_fd);
642     }
643 }
644 
645 /* vstream_buf_wipe - reset buffer to initial state */
646 
647 static void vstream_buf_wipe(VBUF *bp)
648 {
649     if ((bp->flags & VBUF_FLAG_FIXED) == 0 && bp->data)
650 	myfree((void *) bp->data);
651     VSTREAM_BUF_ZERO(bp);
652     VSTREAM_BUF_ACTIONS(bp, 0, 0, 0);
653 }
654 
655 /* vstream_fflush_some - flush some buffered data */
656 
657 static int vstream_fflush_some(VSTREAM *stream, ssize_t to_flush)
658 {
659     const char *myname = "vstream_fflush_some";
660     VBUF   *bp = &stream->buf;
661     ssize_t used;
662     ssize_t left_over;
663     void   *data;
664     ssize_t len;
665     ssize_t n;
666     int     timeout;
667     struct timeval before;
668     struct timeval elapsed;
669 
670     /*
671      * Sanity checks. It is illegal to flush a read-only stream. Otherwise,
672      * if there is buffered input, discard the input. If there is buffered
673      * output, require that the amount to flush is larger than the amount to
674      * keep, so that we can memcpy() the residue.
675      */
676     if (bp->put_ready == 0)
677 	msg_panic("%s: read-only stream", myname);
678     switch (bp->flags & (VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ)) {
679     case VSTREAM_FLAG_READ:			/* discard input */
680 	VSTREAM_BUF_AT_END(bp);
681 	/* FALLTHROUGH */
682     case 0:					/* flush after seek? */
683 	return ((bp->flags & VSTREAM_FLAG_ERR) ? VSTREAM_EOF : 0);
684     case VSTREAM_FLAG_WRITE:			/* output buffered */
685 	break;
686     case VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ:
687 	msg_panic("%s: read/write stream", myname);
688     }
689     used = bp->len - bp->cnt;
690     left_over = used - to_flush;
691 
692     if (msg_verbose > 2 && stream != VSTREAM_ERR)
693 	msg_info("%s: fd %d flush %ld", myname, stream->fd, (long) to_flush);
694     if (to_flush < 0 || left_over < 0)
695 	msg_panic("%s: bad to_flush %ld", myname, (long) to_flush);
696     if (to_flush < left_over)
697 	msg_panic("%s: to_flush < left_over", myname);
698     if (to_flush == 0)
699 	return ((bp->flags & VSTREAM_FLAG_ERR) ? VSTREAM_EOF : 0);
700     if (bp->flags & VSTREAM_FLAG_ERR)
701 	return (VSTREAM_EOF);
702 
703     /*
704      * When flushing a buffer, allow for partial writes. These can happen
705      * while talking to a network. Update the cached file seek position, if
706      * any.
707      *
708      * When deadlines are enabled, we count the elapsed time for each write
709      * operation instead of simply comparing the time-of-day clock with a
710      * per-stream deadline. The latter could result in anomalies when an
711      * application does lengthy processing between write operations. Keep in
712      * mind that a receiver may not be able to keep up when a sender suddenly
713      * floods it with a lot of data as it tries to catch up with a deadline.
714      */
715     for (data = (void *) bp->data, len = to_flush; len > 0; len -= n, data += n) {
716 	if (bp->flags & VSTREAM_FLAG_DEADLINE) {
717 	    timeout = stream->time_limit.tv_sec + (stream->time_limit.tv_usec > 0);
718 	    if (timeout <= 0) {
719 		bp->flags |= (VSTREAM_FLAG_WR_ERR | VSTREAM_FLAG_WR_TIMEOUT);
720 		errno = ETIMEDOUT;
721 		return (VSTREAM_EOF);
722 	    }
723 	    if (len == to_flush)
724 		GETTIMEOFDAY(&before);
725 	    else
726 		before = stream->iotime;
727 	} else
728 	    timeout = stream->timeout;
729 	if ((n = stream->write_fn(stream->fd, data, len, timeout, stream->context)) <= 0) {
730 	    bp->flags |= VSTREAM_FLAG_WR_ERR;
731 	    if (errno == ETIMEDOUT) {
732 		bp->flags |= VSTREAM_FLAG_WR_TIMEOUT;
733 		stream->time_limit.tv_sec = stream->time_limit.tv_usec = 0;
734 	    }
735 	    return (VSTREAM_EOF);
736 	}
737 	if (timeout) {
738 	    GETTIMEOFDAY(&stream->iotime);
739 	    if (bp->flags & VSTREAM_FLAG_DEADLINE) {
740 		VSTREAM_SUB_TIME(elapsed, stream->iotime, before);
741 		VSTREAM_SUB_TIME(stream->time_limit, stream->time_limit, elapsed);
742 	    }
743 	}
744 	if (msg_verbose > 2 && stream != VSTREAM_ERR && n != to_flush)
745 	    msg_info("%s: %d flushed %ld/%ld", myname, stream->fd,
746 		     (long) n, (long) to_flush);
747     }
748     if (bp->flags & VSTREAM_FLAG_SEEK)
749 	stream->offset += to_flush;
750 
751     /*
752      * Allow for partial buffer flush requests. We use memcpy() for reasons
753      * of portability to pre-ANSI environments (SunOS 4.x or Ultrix 4.x :-).
754      * This is OK because we have already verified that the to_flush count is
755      * larger than the left_over count.
756      */
757     if (left_over > 0)
758 	memcpy(bp->data, bp->data + to_flush, left_over);
759     bp->cnt += to_flush;
760     bp->ptr -= to_flush;
761     return ((bp->flags & VSTREAM_FLAG_ERR) ? VSTREAM_EOF : 0);
762 }
763 
764 /* vstream_fflush_delayed - delayed stream flush for double-buffered stream */
765 
766 static int vstream_fflush_delayed(VSTREAM *stream)
767 {
768     int     status;
769 
770     /*
771      * Sanity check.
772      */
773     if ((stream->buf.flags & VSTREAM_FLAG_READ_DOUBLE) != VSTREAM_FLAG_READ_DOUBLE)
774 	msg_panic("vstream_fflush_delayed: bad flags");
775 
776     /*
777      * Temporarily swap buffers and flush unwritten data. This may seem like
778      * a lot of work, but it's peanuts compared to the write(2) call that we
779      * already have avoided. For example, delayed flush is never used on a
780      * non-pipelined SMTP connection.
781      */
782     stream->buf.flags &= ~VSTREAM_FLAG_READ;
783     VSTREAM_SAVE_STATE(stream, read_buf, read_fd);
784     stream->buf.flags |= VSTREAM_FLAG_WRITE;
785     VSTREAM_RESTORE_STATE(stream, write_buf, write_fd);
786 
787     status = VSTREAM_FFLUSH_SOME(stream);
788 
789     stream->buf.flags &= ~VSTREAM_FLAG_WRITE;
790     VSTREAM_SAVE_STATE(stream, write_buf, write_fd);
791     stream->buf.flags |= VSTREAM_FLAG_READ;
792     VSTREAM_RESTORE_STATE(stream, read_buf, read_fd);
793 
794     return (status);
795 }
796 
797 /* vstream_buf_get_ready - vbuf callback to make buffer ready for reading */
798 
799 static int vstream_buf_get_ready(VBUF *bp)
800 {
801     VSTREAM *stream = VBUF_TO_APPL(bp, VSTREAM, buf);
802     const char *myname = "vstream_buf_get_ready";
803     ssize_t n;
804     struct timeval before;
805     struct timeval elapsed;
806     int     timeout;
807 
808     /*
809      * Detect a change of I/O direction or position. If so, flush any
810      * unwritten output immediately when the stream is single-buffered, or
811      * when the stream is double-buffered and the read buffer is empty.
812      */
813     switch (bp->flags & (VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ)) {
814     case VSTREAM_FLAG_WRITE:			/* change direction */
815 	if (bp->ptr > bp->data)
816 	    if ((bp->flags & VSTREAM_FLAG_DOUBLE) == 0
817 		|| stream->read_buf.cnt >= 0)
818 		if (VSTREAM_FFLUSH_SOME(stream))
819 		    return (VSTREAM_EOF);
820 	bp->flags &= ~VSTREAM_FLAG_WRITE;
821 	if (bp->flags & VSTREAM_FLAG_DOUBLE)
822 	    VSTREAM_SAVE_STATE(stream, write_buf, write_fd);
823 	/* FALLTHROUGH */
824     case 0:					/* change position */
825 	bp->flags |= VSTREAM_FLAG_READ;
826 	if (bp->flags & VSTREAM_FLAG_DOUBLE) {
827 	    VSTREAM_RESTORE_STATE(stream, read_buf, read_fd);
828 	    if (bp->cnt < 0)
829 		return (0);
830 	}
831 	/* FALLTHROUGH */
832     case VSTREAM_FLAG_READ:			/* no change */
833 	break;
834     case VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ:
835 	msg_panic("%s: read/write stream", myname);
836     }
837 
838     /*
839      * If this is the first GET operation, allocate a buffer. Late buffer
840      * allocation gives the application a chance to override the default
841      * buffering policy.
842      *
843      * XXX Subtle code to set the preferred buffer size as late as possible.
844      */
845     if (stream->req_bufsize == 0)
846 	stream->req_bufsize = VSTREAM_BUFSIZE;
847     if (bp->len < stream->req_bufsize)
848 	vstream_buf_alloc(bp, stream->req_bufsize);
849 
850     /*
851      * If the stream is double-buffered and the write buffer is not empty,
852      * this is the time to flush the write buffer. Delayed flushes reduce
853      * system call overhead, and on TCP sockets, avoid triggering Nagle's
854      * algorithm.
855      */
856     if ((bp->flags & VSTREAM_FLAG_DOUBLE)
857 	&& stream->write_buf.len > stream->write_buf.cnt)
858 	if (vstream_fflush_delayed(stream))
859 	    return (VSTREAM_EOF);
860 
861     /*
862      * Did we receive an EOF indication?
863      */
864     if (bp->flags & VSTREAM_FLAG_EOF)
865 	return (VSTREAM_EOF);
866 
867     /*
868      * Fill the buffer with as much data as we can handle, or with as much
869      * data as is available right now, whichever is less. Update the cached
870      * file seek position, if any.
871      *
872      * When deadlines are enabled, we count the elapsed time for each read
873      * operation instead of simply comparing the time-of-day clock with a
874      * per-stream deadline. The latter could result in anomalies when an
875      * application does lengthy processing between read operations. Keep in
876      * mind that a sender may get blocked, and may not be able to keep up
877      * when a receiver suddenly wants to read a lot of data as it tries to
878      * catch up with a deadline.
879      */
880     if (bp->flags & VSTREAM_FLAG_DEADLINE) {
881 	timeout = stream->time_limit.tv_sec + (stream->time_limit.tv_usec > 0);
882 	if (timeout <= 0) {
883 	    bp->flags |= (VSTREAM_FLAG_RD_ERR | VSTREAM_FLAG_RD_TIMEOUT);
884 	    errno = ETIMEDOUT;
885 	    return (VSTREAM_EOF);
886 	}
887 	GETTIMEOFDAY(&before);
888     } else
889 	timeout = stream->timeout;
890     switch (n = stream->read_fn(stream->fd, bp->data, bp->len, timeout, stream->context)) {
891     case -1:
892 	bp->flags |= VSTREAM_FLAG_RD_ERR;
893 	if (errno == ETIMEDOUT) {
894 	    bp->flags |= VSTREAM_FLAG_RD_TIMEOUT;
895 	    stream->time_limit.tv_sec = stream->time_limit.tv_usec = 0;
896 	}
897 	return (VSTREAM_EOF);
898     case 0:
899 	bp->flags |= VSTREAM_FLAG_EOF;
900 	return (VSTREAM_EOF);
901     default:
902 	if (timeout) {
903 	    GETTIMEOFDAY(&stream->iotime);
904 	    if (bp->flags & VSTREAM_FLAG_DEADLINE) {
905 		VSTREAM_SUB_TIME(elapsed, stream->iotime, before);
906 		VSTREAM_SUB_TIME(stream->time_limit, stream->time_limit, elapsed);
907 	    }
908 	}
909 	if (msg_verbose > 2)
910 	    msg_info("%s: fd %d got %ld", myname, stream->fd, (long) n);
911 	bp->cnt = -n;
912 	bp->ptr = bp->data;
913 	if (bp->flags & VSTREAM_FLAG_SEEK)
914 	    stream->offset += n;
915 	return (0);
916     }
917 }
918 
919 /* vstream_buf_put_ready - vbuf callback to make buffer ready for writing */
920 
921 static int vstream_buf_put_ready(VBUF *bp)
922 {
923     VSTREAM *stream = VBUF_TO_APPL(bp, VSTREAM, buf);
924     const char *myname = "vstream_buf_put_ready";
925 
926     /*
927      * Sanity checks. Detect a change of I/O direction or position. If so,
928      * discard unread input, and reset the buffer to the beginning.
929      */
930     switch (bp->flags & (VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ)) {
931     case VSTREAM_FLAG_READ:			/* change direction */
932 	bp->flags &= ~VSTREAM_FLAG_READ;
933 	if (bp->flags & VSTREAM_FLAG_DOUBLE)
934 	    VSTREAM_SAVE_STATE(stream, read_buf, read_fd);
935 	/* FALLTHROUGH */
936     case 0:					/* change position */
937 	bp->flags |= VSTREAM_FLAG_WRITE;
938 	if (bp->flags & VSTREAM_FLAG_DOUBLE)
939 	    VSTREAM_RESTORE_STATE(stream, write_buf, write_fd);
940 	else
941 	    VSTREAM_BUF_AT_START(bp);
942 	/* FALLTHROUGH */
943     case VSTREAM_FLAG_WRITE:			/* no change */
944 	break;
945     case VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ:
946 	msg_panic("%s: read/write stream", myname);
947     }
948 
949     /*
950      * Remember the direction. If this is the first PUT operation for this
951      * stream or if the buffer is smaller than the requested size, allocate a
952      * new buffer; obviously there is no data to be flushed yet. Otherwise,
953      * flush the buffer.
954      *
955      * XXX Subtle code to set the preferred buffer size as late as possible.
956      */
957     if (stream->req_bufsize == 0)
958 	stream->req_bufsize = VSTREAM_BUFSIZE;
959     if (bp->len < stream->req_bufsize) {
960 	vstream_buf_alloc(bp, stream->req_bufsize);
961     } else if (bp->cnt <= 0) {
962 	if (VSTREAM_FFLUSH_SOME(stream))
963 	    return (VSTREAM_EOF);
964     }
965     return (0);
966 }
967 
968 /* vstream_buf_space - reserve space ahead of time */
969 
970 static int vstream_buf_space(VBUF *bp, ssize_t want)
971 {
972     VSTREAM *stream = VBUF_TO_APPL(bp, VSTREAM, buf);
973     ssize_t used;
974     ssize_t incr;
975     ssize_t shortage;
976     const char *myname = "vstream_buf_space";
977 
978     /*
979      * Sanity checks. Reserving space implies writing. It is illegal to write
980      * to a read-only stream. Detect a change of I/O direction or position.
981      * If so, reset the buffer to the beginning.
982      */
983     if (bp->put_ready == 0)
984 	msg_panic("%s: read-only stream", myname);
985     switch (bp->flags & (VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE)) {
986     case VSTREAM_FLAG_READ:			/* change direction */
987 	bp->flags &= ~VSTREAM_FLAG_READ;
988 	if (bp->flags & VSTREAM_FLAG_DOUBLE)
989 	    VSTREAM_SAVE_STATE(stream, read_buf, read_fd);
990 	/* FALLTHROUGH */
991     case 0:					/* change position */
992 	bp->flags |= VSTREAM_FLAG_WRITE;
993 	if (bp->flags & VSTREAM_FLAG_DOUBLE)
994 	    VSTREAM_RESTORE_STATE(stream, write_buf, write_fd);
995 	else
996 	    VSTREAM_BUF_AT_START(bp);
997 	/* FALLTHROUGH */
998     case VSTREAM_FLAG_WRITE:			/* no change */
999 	break;
1000     case VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE:
1001 	msg_panic("%s: read/write stream", myname);
1002     }
1003 
1004     /*
1005      * See if enough space is available. If not, flush a multiple of
1006      * VSTREAM_BUFSIZE bytes and resize the buffer to a multiple of
1007      * VSTREAM_BUFSIZE. We flush multiples of VSTREAM_BUFSIZE in an attempt
1008      * to keep file updates block-aligned for better performance.
1009      *
1010      * XXX Subtle code to set the preferred buffer size as late as possible.
1011      */
1012 #define VSTREAM_TRUNCATE(count, base)	(((count) / (base)) * (base))
1013 #define VSTREAM_ROUNDUP(count, base)	VSTREAM_TRUNCATE(count + base - 1, base)
1014 
1015     if (stream->req_bufsize == 0)
1016 	stream->req_bufsize = VSTREAM_BUFSIZE;
1017     if (want > bp->cnt) {
1018 	if ((used = bp->len - bp->cnt) > stream->req_bufsize)
1019 	    if (vstream_fflush_some(stream, VSTREAM_TRUNCATE(used, stream->req_bufsize)))
1020 		return (VSTREAM_EOF);
1021 	if ((shortage = (want - bp->cnt)) > 0) {
1022 	    if ((bp->flags & VSTREAM_FLAG_FIXED)
1023 		|| shortage > __MAXINT__(ssize_t) -bp->len - stream->req_bufsize) {
1024 		bp->flags |= VSTREAM_FLAG_WR_ERR;
1025 	    } else {
1026 		incr = VSTREAM_ROUNDUP(shortage, stream->req_bufsize);
1027 		vstream_buf_alloc(bp, bp->len + incr);
1028 	    }
1029 	}
1030     }
1031     return (vstream_ferror(stream) ? VSTREAM_EOF : 0);	/* mmap() may fail */
1032 }
1033 
1034 /* vstream_fpurge - discard unread or unwritten content */
1035 
1036 int     vstream_fpurge(VSTREAM *stream, int direction)
1037 {
1038     const char *myname = "vstream_fpurge";
1039     VBUF   *bp = &stream->buf;
1040 
1041 #define VSTREAM_MAYBE_PURGE_WRITE(d, b) if ((d) & VSTREAM_PURGE_WRITE) \
1042 	VSTREAM_BUF_AT_START((b))
1043 #define VSTREAM_MAYBE_PURGE_READ(d, b) if ((d) & VSTREAM_PURGE_READ) \
1044 	VSTREAM_BUF_AT_END((b))
1045 
1046     /*
1047      * To discard all unread contents, position the read buffer at its end,
1048      * so that we skip over any unread data, and so that the next read
1049      * operation will refill the buffer.
1050      *
1051      * To discard all unwritten content, position the write buffer at its
1052      * beginning, so that the next write operation clobbers any unwritten
1053      * data.
1054      */
1055     switch (bp->flags & (VSTREAM_FLAG_READ_DOUBLE | VSTREAM_FLAG_WRITE)) {
1056     case VSTREAM_FLAG_READ_DOUBLE:
1057 	VSTREAM_MAYBE_PURGE_WRITE(direction, &stream->write_buf);
1058 	/* FALLTHROUGH */
1059     case VSTREAM_FLAG_READ:
1060 	VSTREAM_MAYBE_PURGE_READ(direction, bp);
1061 	break;
1062     case VSTREAM_FLAG_DOUBLE:
1063 	VSTREAM_MAYBE_PURGE_WRITE(direction, &stream->write_buf);
1064 	VSTREAM_MAYBE_PURGE_READ(direction, &stream->read_buf);
1065 	break;
1066     case VSTREAM_FLAG_WRITE_DOUBLE:
1067 	VSTREAM_MAYBE_PURGE_READ(direction, &stream->read_buf);
1068 	/* FALLTHROUGH */
1069     case VSTREAM_FLAG_WRITE:
1070 	VSTREAM_MAYBE_PURGE_WRITE(direction, bp);
1071 	break;
1072     case VSTREAM_FLAG_READ_DOUBLE | VSTREAM_FLAG_WRITE:
1073     case VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE:
1074 	msg_panic("%s: read/write stream", myname);
1075     }
1076 
1077     /*
1078      * Invalidate the cached file seek position.
1079      */
1080     bp->flags &= ~VSTREAM_FLAG_SEEK;
1081     stream->offset = 0;
1082 
1083     return (0);
1084 }
1085 
1086 /* vstream_fseek - change I/O position */
1087 
1088 off_t   vstream_fseek(VSTREAM *stream, off_t offset, int whence)
1089 {
1090     const char *myname = "vstream_fseek";
1091     VBUF   *bp = &stream->buf;
1092 
1093     /*
1094      * Flush any unwritten output. Discard any unread input. Position the
1095      * buffer at the end, so that the next GET or PUT operation triggers a
1096      * buffer boundary action.
1097      */
1098     switch (bp->flags & (VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE)) {
1099     case VSTREAM_FLAG_WRITE:
1100 	if (bp->ptr > bp->data) {
1101 	    if (whence == SEEK_CUR)
1102 		offset += (bp->ptr - bp->data);	/* add unwritten data */
1103 	    else if (whence == SEEK_END)
1104 		bp->flags &= ~VSTREAM_FLAG_SEEK;
1105 	    if (VSTREAM_FFLUSH_SOME(stream))
1106 		return (-1);
1107 	}
1108 	VSTREAM_BUF_AT_END(bp);
1109 	break;
1110     case VSTREAM_FLAG_READ:
1111 	if (whence == SEEK_CUR)
1112 	    offset += bp->cnt;			/* subtract unread data */
1113 	else if (whence == SEEK_END)
1114 	    bp->flags &= ~VSTREAM_FLAG_SEEK;
1115 	/* FALLTHROUGH */
1116     case 0:
1117 	VSTREAM_BUF_AT_END(bp);
1118 	break;
1119     case VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE:
1120 	msg_panic("%s: read/write stream", myname);
1121     }
1122 
1123     /*
1124      * Clear the read/write flags to inform the buffer boundary action
1125      * routines that we may have changed I/O position.
1126      */
1127     bp->flags &= ~(VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE);
1128 
1129     /*
1130      * Shave an unnecessary system call.
1131      */
1132     if (bp->flags & VSTREAM_FLAG_NSEEK) {
1133 	errno = ESPIPE;
1134 	return (-1);
1135     }
1136 
1137     /*
1138      * Update the cached file seek position.
1139      */
1140     if ((stream->offset = lseek(stream->fd, offset, whence)) < 0) {
1141 	if (errno == ESPIPE)
1142 	    bp->flags |= VSTREAM_FLAG_NSEEK;
1143     } else {
1144 	bp->flags |= VSTREAM_FLAG_SEEK;
1145     }
1146     bp->flags &= ~VSTREAM_FLAG_EOF;
1147     return (stream->offset);
1148 }
1149 
1150 /* vstream_ftell - return file offset */
1151 
1152 off_t   vstream_ftell(VSTREAM *stream)
1153 {
1154     VBUF   *bp = &stream->buf;
1155 
1156     /*
1157      * Shave an unnecessary syscall.
1158      */
1159     if (bp->flags & VSTREAM_FLAG_NSEEK) {
1160 	errno = ESPIPE;
1161 	return (-1);
1162     }
1163 
1164     /*
1165      * Use the cached file offset when available. This is the offset after
1166      * the last read, write or seek operation.
1167      */
1168     if ((bp->flags & VSTREAM_FLAG_SEEK) == 0) {
1169 	if ((stream->offset = lseek(stream->fd, (off_t) 0, SEEK_CUR)) < 0) {
1170 	    bp->flags |= VSTREAM_FLAG_NSEEK;
1171 	    return (-1);
1172 	}
1173 	bp->flags |= VSTREAM_FLAG_SEEK;
1174     }
1175 
1176     /*
1177      * If this is a read buffer, subtract the number of unread bytes from the
1178      * cached offset. Remember that read counts are negative.
1179      */
1180     if (bp->flags & VSTREAM_FLAG_READ)
1181 	return (stream->offset + bp->cnt);
1182 
1183     /*
1184      * If this is a write buffer, add the number of unwritten bytes to the
1185      * cached offset.
1186      */
1187     if (bp->flags & VSTREAM_FLAG_WRITE)
1188 	return (stream->offset + (bp->ptr - bp->data));
1189 
1190     /*
1191      * Apparently, this is a new buffer, or a buffer after seek, so there is
1192      * no need to account for unread or unwritten data.
1193      */
1194     return (stream->offset);
1195 }
1196 
1197 /* vstream_fdopen - add buffering to pre-opened stream */
1198 
1199 VSTREAM *vstream_fdopen(int fd, int flags)
1200 {
1201     VSTREAM *stream;
1202 
1203     /*
1204      * Sanity check.
1205      */
1206     if (fd < 0)
1207 	msg_panic("vstream_fdopen: bad file %d", fd);
1208 
1209     /*
1210      * Initialize buffers etc. but do as little as possible. Late buffer
1211      * allocation etc. gives the application a chance to override default
1212      * policies. Either this, or the vstream*open() routines would have to
1213      * have a really ugly interface with lots of mostly-unused arguments (can
1214      * you say VMS?).
1215      */
1216     stream = (VSTREAM *) mymalloc(sizeof(*stream));
1217     stream->fd = fd;
1218     stream->read_fn = VSTREAM_CAN_READ(flags) ? (VSTREAM_RW_FN) timed_read : 0;
1219     stream->write_fn = VSTREAM_CAN_WRITE(flags) ? (VSTREAM_RW_FN) timed_write : 0;
1220     vstream_buf_init(&stream->buf, flags);
1221     stream->offset = 0;
1222     stream->path = 0;
1223     stream->pid = 0;
1224     stream->waitpid_fn = 0;
1225     stream->timeout = 0;
1226     stream->context = 0;
1227     stream->jbuf = 0;
1228     stream->iotime.tv_sec = stream->iotime.tv_usec = 0;
1229     stream->time_limit.tv_sec = stream->time_limit.tv_usec = 0;
1230     stream->req_bufsize = 0;
1231     return (stream);
1232 }
1233 
1234 /* vstream_fopen - open buffered file stream */
1235 
1236 VSTREAM *vstream_fopen(const char *path, int flags, mode_t mode)
1237 {
1238     VSTREAM *stream;
1239     int     fd;
1240 
1241     if ((fd = open(path, flags, mode)) < 0) {
1242 	return (0);
1243     } else {
1244 	stream = vstream_fdopen(fd, flags);
1245 	stream->path = mystrdup(path);
1246 	return (stream);
1247     }
1248 }
1249 
1250 /* vstream_fflush - flush write buffer */
1251 
1252 int     vstream_fflush(VSTREAM *stream)
1253 {
1254     if ((stream->buf.flags & VSTREAM_FLAG_READ_DOUBLE)
1255 	== VSTREAM_FLAG_READ_DOUBLE
1256 	&& stream->write_buf.len > stream->write_buf.cnt)
1257 	vstream_fflush_delayed(stream);
1258     return (VSTREAM_FFLUSH_SOME(stream));
1259 }
1260 
1261 /* vstream_fclose - close buffered stream */
1262 
1263 int     vstream_fclose(VSTREAM *stream)
1264 {
1265     int     err;
1266 
1267     /*
1268      * NOTE: Negative file descriptors are not part of the external
1269      * interface. They are for internal use only, in order to support
1270      * vstream_fdclose() without a lot of code duplication. Applications that
1271      * rely on negative VSTREAM file descriptors will break without warning.
1272      */
1273     if (stream->pid != 0)
1274 	msg_panic("vstream_fclose: stream has process");
1275     if ((stream->buf.flags & VSTREAM_FLAG_WRITE_DOUBLE) != 0 && stream->fd >= 0)
1276 	vstream_fflush(stream);
1277     /* Do not remove: vstream_fdclose() depends on this error test. */
1278     err = vstream_ferror(stream);
1279     if (stream->buf.flags & VSTREAM_FLAG_DOUBLE) {
1280 	if (stream->read_fd >= 0)
1281 	    err |= close(stream->read_fd);
1282 	if (stream->write_fd != stream->read_fd)
1283 	    if (stream->write_fd >= 0)
1284 		err |= close(stream->write_fd);
1285 	vstream_buf_wipe(&stream->read_buf);
1286 	vstream_buf_wipe(&stream->write_buf);
1287 	stream->buf = stream->read_buf;
1288     } else {
1289 	if (stream->fd >= 0)
1290 	    err |= close(stream->fd);
1291 	vstream_buf_wipe(&stream->buf);
1292     }
1293     if (stream->path)
1294 	myfree(stream->path);
1295     if (stream->jbuf)
1296 	myfree((void *) stream->jbuf);
1297     if (!VSTREAM_STATIC(stream))
1298 	myfree((void *) stream);
1299     return (err ? VSTREAM_EOF : 0);
1300 }
1301 
1302 /* vstream_fdclose - close stream, leave file(s) open */
1303 
1304 int     vstream_fdclose(VSTREAM *stream)
1305 {
1306 
1307     /*
1308      * Flush unwritten output, just like vstream_fclose(). Errors are
1309      * reported by vstream_fclose().
1310      */
1311     if ((stream->buf.flags & VSTREAM_FLAG_WRITE_DOUBLE) != 0)
1312 	(void) vstream_fflush(stream);
1313 
1314     /*
1315      * NOTE: Negative file descriptors are not part of the external
1316      * interface. They are for internal use only, in order to support
1317      * vstream_fdclose() without a lot of code duplication. Applications that
1318      * rely on negative VSTREAM file descriptors will break without warning.
1319      */
1320     if (stream->buf.flags & VSTREAM_FLAG_DOUBLE) {
1321 	stream->fd = stream->read_fd = stream->write_fd = -1;
1322     } else {
1323 	stream->fd = -1;
1324     }
1325     return (vstream_fclose(stream));
1326 }
1327 
1328 /* vstream_printf - formatted print to stdout */
1329 
1330 VSTREAM *vstream_printf(const char *fmt,...)
1331 {
1332     VSTREAM *stream = VSTREAM_OUT;
1333     va_list ap;
1334 
1335     va_start(ap, fmt);
1336     vbuf_print(&stream->buf, fmt, ap);
1337     va_end(ap);
1338     return (stream);
1339 }
1340 
1341 /* vstream_fprintf - formatted print to buffered stream */
1342 
1343 VSTREAM *vstream_fprintf(VSTREAM *stream, const char *fmt,...)
1344 {
1345     va_list ap;
1346 
1347     va_start(ap, fmt);
1348     vbuf_print(&stream->buf, fmt, ap);
1349     va_end(ap);
1350     return (stream);
1351 }
1352 
1353 /* vstream_fputs - write string to stream */
1354 
1355 int     vstream_fputs(const char *str, VSTREAM *stream)
1356 {
1357     int     ch;
1358 
1359     while ((ch = *str++) != 0)
1360 	if (VSTREAM_PUTC(ch, stream) == VSTREAM_EOF)
1361 	    return (VSTREAM_EOF);
1362     return (0);
1363 }
1364 
1365 /* vstream_control - fine control */
1366 
1367 void    vstream_control(VSTREAM *stream, int name,...)
1368 {
1369     const char *myname = "vstream_control";
1370     va_list ap;
1371     int     floor;
1372     int     old_fd;
1373     ssize_t req_bufsize = 0;
1374     VSTREAM *stream2;
1375 
1376 #define SWAP(type,a,b) do { type temp = (a); (a) = (b); (b) = (temp); } while (0)
1377 
1378     for (va_start(ap, name); name != VSTREAM_CTL_END; name = va_arg(ap, int)) {
1379 	switch (name) {
1380 	case VSTREAM_CTL_READ_FN:
1381 	    stream->read_fn = va_arg(ap, VSTREAM_RW_FN);
1382 	    break;
1383 	case VSTREAM_CTL_WRITE_FN:
1384 	    stream->write_fn = va_arg(ap, VSTREAM_RW_FN);
1385 	    break;
1386 	case VSTREAM_CTL_CONTEXT:
1387 	    stream->context = va_arg(ap, void *);
1388 	    break;
1389 	case VSTREAM_CTL_PATH:
1390 	    if (stream->path)
1391 		myfree(stream->path);
1392 	    stream->path = mystrdup(va_arg(ap, char *));
1393 	    break;
1394 	case VSTREAM_CTL_DOUBLE:
1395 	    if ((stream->buf.flags & VSTREAM_FLAG_DOUBLE) == 0) {
1396 		stream->buf.flags |= VSTREAM_FLAG_DOUBLE;
1397 		if (stream->buf.flags & VSTREAM_FLAG_READ) {
1398 		    VSTREAM_SAVE_STATE(stream, read_buf, read_fd);
1399 		    VSTREAM_FORK_STATE(stream, write_buf, write_fd);
1400 		} else {
1401 		    VSTREAM_SAVE_STATE(stream, write_buf, write_fd);
1402 		    VSTREAM_FORK_STATE(stream, read_buf, read_fd);
1403 		}
1404 	    }
1405 	    break;
1406 	case VSTREAM_CTL_READ_FD:
1407 	    if ((stream->buf.flags & VSTREAM_FLAG_DOUBLE) == 0)
1408 		msg_panic("VSTREAM_CTL_READ_FD requires double buffering");
1409 	    stream->read_fd = va_arg(ap, int);
1410 	    stream->buf.flags |= VSTREAM_FLAG_NSEEK;
1411 	    break;
1412 	case VSTREAM_CTL_WRITE_FD:
1413 	    if ((stream->buf.flags & VSTREAM_FLAG_DOUBLE) == 0)
1414 		msg_panic("VSTREAM_CTL_WRITE_FD requires double buffering");
1415 	    stream->write_fd = va_arg(ap, int);
1416 	    stream->buf.flags |= VSTREAM_FLAG_NSEEK;
1417 	    break;
1418 	case VSTREAM_CTL_SWAP_FD:
1419 	    stream2 = va_arg(ap, VSTREAM *);
1420 	    if ((stream->buf.flags & VSTREAM_FLAG_DOUBLE)
1421 		!= (stream2->buf.flags & VSTREAM_FLAG_DOUBLE))
1422 		msg_panic("VSTREAM_CTL_SWAP_FD can't swap descriptors between "
1423 			  "single-buffered and double-buffered streams");
1424 	    if (stream->buf.flags & VSTREAM_FLAG_DOUBLE) {
1425 		SWAP(int, stream->read_fd, stream2->read_fd);
1426 		SWAP(int, stream->write_fd, stream2->write_fd);
1427 		stream->fd = ((stream->buf.flags & VSTREAM_FLAG_WRITE) ?
1428 			      stream->write_fd : stream->read_fd);
1429 	    } else {
1430 		SWAP(int, stream->fd, stream2->fd);
1431 	    }
1432 	    break;
1433 	case VSTREAM_CTL_TIMEOUT:
1434 	    if (stream->timeout == 0)
1435 		GETTIMEOFDAY(&stream->iotime);
1436 	    stream->timeout = va_arg(ap, int);
1437 	    if (stream->timeout < 0)
1438 		msg_panic("%s: bad timeout %d", myname, stream->timeout);
1439 	    break;
1440 	case VSTREAM_CTL_EXCEPT:
1441 	    if (stream->jbuf == 0)
1442 		stream->jbuf =
1443 		    (VSTREAM_JMP_BUF *) mymalloc(sizeof(VSTREAM_JMP_BUF));
1444 	    break;
1445 
1446 #ifdef VSTREAM_CTL_DUPFD
1447 
1448 #define VSTREAM_TRY_DUPFD(backup, fd, floor) do { \
1449 	if (((backup) = (fd)) < floor) { \
1450 	    if (((fd) = fcntl((backup), F_DUPFD, (floor))) < 0) \
1451 		msg_fatal("fcntl F_DUPFD %d: %m", (floor)); \
1452 	    (void) close(backup); \
1453 	} \
1454     } while (0)
1455 
1456 	case VSTREAM_CTL_DUPFD:
1457 	    floor = va_arg(ap, int);
1458 	    if (stream->buf.flags & VSTREAM_FLAG_DOUBLE) {
1459 		VSTREAM_TRY_DUPFD(old_fd, stream->read_fd, floor);
1460 		if (stream->write_fd == old_fd)
1461 		    stream->write_fd = stream->read_fd;
1462 		else
1463 		    VSTREAM_TRY_DUPFD(old_fd, stream->write_fd, floor);
1464 		stream->fd = (stream->buf.flags & VSTREAM_FLAG_READ) ?
1465 		    stream->read_fd : stream->write_fd;
1466 	    } else {
1467 		VSTREAM_TRY_DUPFD(old_fd, stream->fd, floor);
1468 	    }
1469 	    break;
1470 #endif
1471 
1472 	    /*
1473 	     * Postpone memory (re)allocation until the space is needed.
1474 	     */
1475 	case VSTREAM_CTL_BUFSIZE:
1476 	    req_bufsize = va_arg(ap, ssize_t);
1477 	    /* Heuristic to detect missing (ssize_t) type cast on LP64 hosts. */
1478 	    if (req_bufsize < 0 || req_bufsize > INT_MAX)
1479 		msg_panic("unreasonable VSTREAM_CTL_BUFSIZE request: %ld",
1480 			  (long) req_bufsize);
1481 	    if ((stream->buf.flags & VSTREAM_FLAG_FIXED) == 0
1482 		&& req_bufsize > stream->req_bufsize) {
1483 		if (msg_verbose)
1484 		    msg_info("fd=%d: stream buffer size old=%ld new=%ld",
1485 			     vstream_fileno(stream),
1486 			     (long) stream->req_bufsize,
1487 			     (long) req_bufsize);
1488 		stream->req_bufsize = req_bufsize;
1489 	    }
1490 	    break;
1491 
1492 	    /*
1493 	     * Make no gettimeofday() etc. system call until we really know
1494 	     * that we need to do I/O. This avoids a performance hit when
1495 	     * sending or receiving body content one line at a time.
1496 	     */
1497 	case VSTREAM_CTL_STOP_DEADLINE:
1498 	    stream->buf.flags &= ~VSTREAM_FLAG_DEADLINE;
1499 	    break;
1500 	case VSTREAM_CTL_START_DEADLINE:
1501 	    if (stream->timeout <= 0)
1502 		msg_panic("%s: bad timeout %d", myname, stream->timeout);
1503 	    stream->buf.flags |= VSTREAM_FLAG_DEADLINE;
1504 	    stream->time_limit.tv_sec = stream->timeout;
1505 	    stream->time_limit.tv_usec = 0;
1506 	    break;
1507 	default:
1508 	    msg_panic("%s: bad name %d", myname, name);
1509 	}
1510     }
1511     va_end(ap);
1512 }
1513 
1514 /* vstream_vprintf - formatted print to stdout */
1515 
1516 VSTREAM *vstream_vprintf(const char *format, va_list ap)
1517 {
1518     VSTREAM *vp = VSTREAM_OUT;
1519 
1520     vbuf_print(&vp->buf, format, ap);
1521     return (vp);
1522 }
1523 
1524 /* vstream_vfprintf - formatted print engine */
1525 
1526 VSTREAM *vstream_vfprintf(VSTREAM *vp, const char *format, va_list ap)
1527 {
1528     vbuf_print(&vp->buf, format, ap);
1529     return (vp);
1530 }
1531 
1532 /* vstream_bufstat - get stream buffer status */
1533 
1534 ssize_t vstream_bufstat(VSTREAM *vp, int command)
1535 {
1536     VBUF   *bp;
1537 
1538     switch (command & VSTREAM_BST_MASK_DIR) {
1539     case VSTREAM_BST_FLAG_IN:
1540 	if (vp->buf.flags & VSTREAM_FLAG_READ) {
1541 	    bp = &vp->buf;
1542 	} else if (vp->buf.flags & VSTREAM_FLAG_DOUBLE) {
1543 	    bp = &vp->read_buf;
1544 	} else {
1545 	    bp = 0;
1546 	}
1547 	switch (command & ~VSTREAM_BST_MASK_DIR) {
1548 	case VSTREAM_BST_FLAG_PEND:
1549 	    return (bp ? -bp->cnt : 0);
1550 	    /* Add other requests below. */
1551 	}
1552 	break;
1553     case VSTREAM_BST_FLAG_OUT:
1554 	if (vp->buf.flags & VSTREAM_FLAG_WRITE) {
1555 	    bp = &vp->buf;
1556 	} else if (vp->buf.flags & VSTREAM_FLAG_DOUBLE) {
1557 	    bp = &vp->write_buf;
1558 	} else {
1559 	    bp = 0;
1560 	}
1561 	switch (command & ~VSTREAM_BST_MASK_DIR) {
1562 	case VSTREAM_BST_FLAG_PEND:
1563 	    return (bp ? bp->len - bp->cnt : 0);
1564 	    /* Add other requests below. */
1565 	}
1566 	break;
1567     }
1568     msg_panic("vstream_bufstat: unknown command: %d", command);
1569 }
1570 
1571 #undef vstream_peek			/* API binary compatibility. */
1572 
1573 /* vstream_peek - peek at a stream */
1574 
1575 ssize_t vstream_peek(VSTREAM *vp)
1576 {
1577     if (vp->buf.flags & VSTREAM_FLAG_READ) {
1578 	return (-vp->buf.cnt);
1579     } else if (vp->buf.flags & VSTREAM_FLAG_DOUBLE) {
1580 	return (-vp->read_buf.cnt);
1581     } else {
1582 	return (0);
1583     }
1584 }
1585 
1586 /* vstream_peek_data - peek at unread data */
1587 
1588 const char *vstream_peek_data(VSTREAM *vp)
1589 {
1590     if (vp->buf.flags & VSTREAM_FLAG_READ) {
1591 	return ((const char *) vp->buf.ptr);
1592     } else if (vp->buf.flags & VSTREAM_FLAG_DOUBLE) {
1593 	return ((const char *) vp->read_buf.ptr);
1594     } else {
1595 	return (0);
1596     }
1597 }
1598 
1599 #ifdef TEST
1600 
1601 static void copy_line(ssize_t bufsize)
1602 {
1603     int     c;
1604 
1605     vstream_control(VSTREAM_IN, VSTREAM_CTL_BUFSIZE(bufsize), VSTREAM_CTL_END);
1606     vstream_control(VSTREAM_OUT, VSTREAM_CTL_BUFSIZE(bufsize), VSTREAM_CTL_END);
1607     while ((c = VSTREAM_GETC(VSTREAM_IN)) != VSTREAM_EOF) {
1608 	VSTREAM_PUTC(c, VSTREAM_OUT);
1609 	if (c == '\n')
1610 	    break;
1611     }
1612     vstream_fflush(VSTREAM_OUT);
1613 }
1614 
1615 static void printf_number(void)
1616 {
1617     vstream_printf("%d\n", __MAXINT__(int));
1618     vstream_fflush(VSTREAM_OUT);
1619 }
1620 
1621  /*
1622   * Exercise some of the features.
1623   */
1624 int     main(int argc, char **argv)
1625 {
1626 
1627     /*
1628      * Test buffer expansion and shrinking. Formatted print may silently
1629      * expand the write buffer and cause multiple bytes to be written.
1630      */
1631     copy_line(1);			/* one-byte read/write */
1632     copy_line(2);				/* two-byte read/write */
1633     copy_line(1);				/* two-byte read/write */
1634     printf_number();				/* multi-byte write */
1635     exit(0);
1636 }
1637 
1638 #endif
1639