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