Home | History | Annotate | Line # | Download | only in util
      1 /*	$NetBSD: vstream.c,v 1.6 2026/05/09 18:49:23 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 /*	VSTREAM	*vstream_memopen(string, flags)
     21 /*	VSTRING	*string;
     22 /*	int	flags;
     23 /*
     24 /*	VSTREAM	*vstream_memreopen(stream, string, flags)
     25 /*	VSTREAM	*stream;
     26 /*	VSTRING	*string;
     27 /*	int	flags;
     28 /*
     29 /*	int	vstream_fclose(stream)
     30 /*	VSTREAM	*stream;
     31 /*
     32 /*	int	vstream_fdclose(stream)
     33 /*	VSTREAM	*stream;
     34 /*
     35 /*	VSTREAM	*vstream_printf(format, ...)
     36 /*	const char *format;
     37 /*
     38 /*	VSTREAM *vstream_fprintf(stream, format, ...)
     39 /*	VSTREAM	*stream;
     40 /*	const char *format;
     41 /*
     42 /*	int	VSTREAM_GETC(stream)
     43 /*	VSTREAM	*stream;
     44 /*
     45 /*	int	VSTREAM_PUTC(ch, stream)
     46 /*	int	ch;
     47 /*
     48 /*	int	VSTREAM_GETCHAR(void)
     49 /*
     50 /*	int	VSTREAM_PUTCHAR(ch)
     51 /*	int	ch;
     52 /*
     53 /*	int	vstream_ungetc(stream, ch)
     54 /*	VSTREAM	*stream;
     55 /*	int	ch;
     56 /*
     57 /*	int	vstream_fputs(str, stream)
     58 /*	const char *str;
     59 /*	VSTREAM	*stream;
     60 /*
     61 /*	off_t	vstream_ftell(stream)
     62 /*	VSTREAM	*stream;
     63 /*
     64 /*	off_t	vstream_fseek(stream, offset, whence)
     65 /*	VSTREAM	*stream;
     66 /*	off_t	offset;
     67 /*	int	whence;
     68 /*
     69 /*	int	vstream_fflush(stream)
     70 /*	VSTREAM	*stream;
     71 /*
     72 /*	int	vstream_fpurge(stream, direction)
     73 /*	VSTREAM	*stream;
     74 /*	int	direction;
     75 /*
     76 /*	ssize_t	vstream_fread(stream, buf, len)
     77 /*	VSTREAM	*stream;
     78 /*	void	*buf;
     79 /*	ssize_t	len;
     80 /*
     81 /*	ssize_t	vstream_fwrite(stream, buf, len)
     82 /*	VSTREAM	*stream;
     83 /*	void *buf;
     84 /*	ssize_t	len;
     85 /*
     86 /*	ssize_t	vstream_fread_app(stream, buf, len)
     87 /*	VSTREAM	*stream;
     88 /*	VSTRING	*buf;
     89 /*	ssize_t	len;
     90 /*
     91 /*	ssize_t	vstream_fread_buf(stream, buf, len)
     92 /*	VSTREAM	*stream;
     93 /*	VSTRING	*buf;
     94 /*	ssize_t	len;
     95 /*
     96 /*	void	vstream_control(stream, name, ...)
     97 /*	VSTREAM	*stream;
     98 /*	int	name;
     99 /*
    100 /*	int	vstream_fileno(stream)
    101 /*	VSTREAM	*stream;
    102 /*
    103 /*	const ssize_t vstream_req_bufsize(stream)
    104 /*	VSTREAM	*stream;
    105 /*
    106 /*	void	*vstream_context(stream)
    107 /*	VSTREAM	*stream;
    108 /*
    109 /*	int	vstream_ferror(stream)
    110 /*	VSTREAM	*stream;
    111 /*
    112 /*	int	vstream_ftimeout(stream)
    113 /*	VSTREAM	*stream;
    114 /*
    115 /*	int	vstream_feof(stream)
    116 /*	VSTREAM	*stream;
    117 /*
    118 /*	int	vstream_clearerr(stream)
    119 /*	VSTREAM	*stream;
    120 /*
    121 /*	const char *VSTREAM_PATH(stream)
    122 /*	VSTREAM	*stream;
    123 /*
    124 /*	char	*vstream_vprintf(format, ap)
    125 /*	const char *format;
    126 /*	va_list	*ap;
    127 /*
    128 /*	char	*vstream_vfprintf(stream, format, ap)
    129 /*	VSTREAM	*stream;
    130 /*	const char *format;
    131 /*	va_list	*ap;
    132 /*
    133 /*	ssize_t	vstream_bufstat(stream, command)
    134 /*	VSTREAM	*stream;
    135 /*	int	command;
    136 /*
    137 /*	ssize_t	vstream_peek(stream)
    138 /*	VSTREAM	*stream;
    139 /*
    140 /*	const char *vstream_peek_data(stream)
    141 /*	VSTREAM	*stream;
    142 /*
    143 /*	int	vstream_setjmp(stream)
    144 /*	VSTREAM	*stream;
    145 /*
    146 /*	void	vstream_longjmp(stream, val)
    147 /*	VSTREAM	*stream;
    148 /*	int	val;
    149 /*
    150 /*	time_t	vstream_ftime(stream)
    151 /*	VSTREAM	*stream;
    152 /*
    153 /*	struct timeval vstream_ftimeval(stream)
    154 /*	VSTREAM	*stream;
    155 /*
    156 /*	int	vstream_rd_error(stream)
    157 /*	VSTREAM	*stream;
    158 /*
    159 /*	int	vstream_wr_error(stream)
    160 /*	VSTREAM	*stream;
    161 /*
    162 /*	int	vstream_rd_timeout(stream)
    163 /*	VSTREAM	*stream;
    164 /*
    165 /*	int	vstream_wr_timeout(stream)
    166 /*	VSTREAM	*stream;
    167 /*
    168 /*	int	vstream_fstat(stream, flags)
    169 /*	VSTREAM	*stream;
    170 /*	int	flags;
    171 /*
    172 /*	void	vstream_no_debug(stream)
    173 /*	VSTREAM	*stream;
    174 /* DESCRIPTION
    175 /*	The \fIvstream\fR module implements light-weight buffered I/O
    176 /*	similar to the standard I/O routines.
    177 /*
    178 /*	The interface is implemented in terms of VSTREAM structure
    179 /*	pointers, also called streams. For convenience, three streams
    180 /*	are predefined: VSTREAM_IN, VSTREAM_OUT, and VSTREAM_ERR. These
    181 /*	streams are connected to the standard input, output and error
    182 /*	file descriptors, respectively.
    183 /*
    184 /*	Although the interface is patterned after the standard I/O
    185 /*	library, there are some major differences:
    186 /* .IP \(bu
    187 /*	File descriptors are not limited to the range 0..255. This
    188 /*	was reason #1 to write these routines in the first place.
    189 /* .IP \(bu
    190 /*	The application can switch between reading and writing on
    191 /*	the same stream without having to perform a flush or seek
    192 /*	operation, and can change write position without having to
    193 /*	flush.  This was reason #2. Upon position or direction change,
    194 /*	unread input is discarded, and unwritten output is flushed
    195 /*	automatically. Exception: with double-buffered streams, unread
    196 /*	input is not discarded upon change of I/O direction, and
    197 /*	output flushing is delayed until the read buffer must be refilled.
    198 /* .IP \(bu
    199 /*	A bidirectional stream can read and write with the same buffer
    200 /*	and file descriptor, or it can have separate read/write
    201 /*	buffers and/or file descriptors.
    202 /* .IP \(bu
    203 /*	No automatic flushing of VSTREAM_OUT upon program exit, or of
    204 /*	VSTREAM_ERR at any time. No unbuffered or line buffered modes.
    205 /*	This functionality may be added when it is really needed.
    206 /* .PP
    207 /*	vstream_fopen() opens the named file and associates a buffered
    208 /*	stream with it.  The \fIpath\fR, \fIflags\fR and \fImode\fR
    209 /*	arguments are passed on to the open(2) routine. The result is
    210 /*	a null pointer in case of problems. The \fIpath\fR argument is
    211 /*	copied and can be looked up with VSTREAM_PATH().
    212 /*
    213 /*	vstream_fdopen() takes an open file and associates a buffered
    214 /*	stream with it. The \fIflags\fR argument specifies how the file
    215 /*	was opened. vstream_fdopen() either succeeds or never returns.
    216 /*
    217 /*	vstream_memopen() opens a VSTRING as a stream. The \fIflags\fR
    218 /*	argument must specify one of O_RDONLY, O_WRONLY, or O_APPEND.
    219 /*	vstream_memopen() either succeeds or never returns. Streams
    220 /*	opened with vstream_memopen() have limitations: they can't
    221 /*	be opened in read/write mode, they can't seek beyond the
    222 /*	end of the VSTRING, and they don't support vstream_control()
    223 /*	methods that manipulate buffers, file descriptors, or I/O
    224 /*	functions. After a VSTRING is opened for writing, its content
    225 /*	will be in an indeterminate state while the stream is open,
    226 /*	and will be null-terminated when the stream is closed.
    227 /*
    228 /*	vstream_memreopen() reopens a memory stream. When the
    229 /*	\fIstream\fR argument is a null pointer, the behavior is that
    230 /*	of vstream_memopen().
    231 /*
    232 /*	vstream_fclose() closes the named buffered stream. The result
    233 /*	is 0 in case of success, VSTREAM_EOF in case of problems.
    234 /*	vstream_fclose() reports the same errors as vstream_ferror().
    235 /*
    236 /*	vstream_fdclose() leaves the file(s) open but is otherwise
    237 /*	identical to vstream_fclose().
    238 /*
    239 /*	vstream_fprintf() formats its arguments according to the
    240 /*	\fIformat\fR argument and writes the result to the named stream.
    241 /*	The result is the stream argument. It understands the s, c, d, u,
    242 /*	o, x, X, e, f and g format types, the l modifier, field width and
    243 /*	precision, sign, and padding with zeros or spaces. In addition,
    244 /*	vstream_fprintf() recognizes the %m format specifier and expands
    245 /*	it to the error message corresponding to the current value of the
    246 /*	global \fIerrno\fR variable.
    247 /*
    248 /*	vstream_printf() performs formatted output to the standard output
    249 /*	stream.
    250 /*
    251 /*	VSTREAM_GETC() reads the next character from the named stream.
    252 /*	The result is VSTREAM_EOF when end-of-file is reached or if a read
    253 /*	error was detected. VSTREAM_GETC() is an unsafe macro that
    254 /*	evaluates some arguments more than once.
    255 /*
    256 /*	VSTREAM_GETCHAR() is an alias for VSTREAM_GETC(VSTREAM_IN).
    257 /*
    258 /*	VSTREAM_PUTC() appends the specified character to the specified
    259 /*	stream. The result is the stored character, or VSTREAM_EOF in
    260 /*	case of problems. VSTREAM_PUTC() is an unsafe macro that
    261 /*	evaluates some arguments more than once.
    262 /*
    263 /*	VSTREAM_PUTCHAR(c) is an alias for VSTREAM_PUTC(c, VSTREAM_OUT).
    264 /*
    265 /*	vstream_ungetc() pushes back a character onto the specified stream
    266 /*	and returns the character, or VSTREAM_EOF in case of problems.
    267 /*	It is an error to push back before reading (or immediately after
    268 /*	changing the stream offset via vstream_fseek()). Upon successful
    269 /*	return, vstream_ungetc() clears the end-of-file stream flag.
    270 /*
    271 /*	vstream_fputs() appends the given null-terminated string to the
    272 /*	specified buffered stream. The result is 0 in case of success,
    273 /*	VSTREAM_EOF in case of problems.
    274 /*
    275 /*	vstream_ftell() returns the file offset for the specified stream,
    276 /*	-1 if the stream is connected to a non-seekable file.
    277 /*
    278 /*	vstream_fseek() changes the file position for the next read or write
    279 /*	operation. Unwritten output is flushed. With unidirectional streams,
    280 /*	unread input is discarded. The \fIoffset\fR argument specifies the file
    281 /*	position from the beginning of the file (\fIwhence\fR is SEEK_SET),
    282 /*	from the current file position (\fIwhence\fR is SEEK_CUR), or from
    283 /*	the file end (SEEK_END). The result value is the file offset
    284 /*	from the beginning of the file, -1 in case of problems.
    285 /*
    286 /*	vstream_fflush() flushes unwritten data to a file that was
    287 /*	opened in read-write or write-only mode.
    288 /*	vstream_fflush() returns 0 in case of success, VSTREAM_EOF in
    289 /*	case of problems. It is an error to flush a read-only stream.
    290 /*	vstream_fflush() reports the same errors as vstream_ferror().
    291 /*
    292 /*	vstream_fpurge() discards the contents of the stream buffer.
    293 /*	If direction is VSTREAM_PURGE_READ, it discards unread data,
    294 /*	else if direction is VSTREAM_PURGE_WRITE, it discards unwritten
    295 /*	data. In the case of a double-buffered stream, if direction is
    296 /*	VSTREAM_PURGE_BOTH, it discards the content of both the read
    297 /*	and write buffers. vstream_fpurge() returns 0 in case of success,
    298 /*	VSTREAM_EOF in case of problems.
    299 /*
    300 /*	vstream_fread() and vstream_fwrite() perform unformatted I/O
    301 /*	on the named stream. The result value is the number of bytes
    302 /*	transferred. A short count is returned in case of end-of-file
    303 /*	or error conditions.
    304 /*
    305 /*	vstream_fread_buf() resets the buffer write position,
    306 /*	allocates space for the specified number of bytes in the
    307 /*	buffer, reads the bytes from the specified VSTREAM, and
    308 /*	adjusts the buffer write position. The buffer is NOT
    309 /*	null-terminated. The result value is as with vstream_fread().
    310 /*	NOTE: do not skip calling vstream_fread_buf() when len == 0.
    311 /*	This function has side effects including resetting the buffer
    312 /*	write position, and skipping the call would invalidate the
    313 /*	buffer state.
    314 /*
    315 /*	vstream_fread_app() is like vstream_fread_buf() but appends
    316 /*	to existing buffer content, instead of writing over it.
    317 /*
    318 /*	vstream_control() allows the user to fine tune the behavior of
    319 /*	the specified stream.  The arguments are a list of macros with
    320 /*	zero or more arguments, terminated with CA_VSTREAM_CTL_END
    321 /*	which has none.  The following lists the names and the types
    322 /*	of the corresponding value arguments.
    323 /* .IP "CA_VSTREAM_CTL_READ_FN(ssize_t (*)(int, void *, size_t, int, void *))"
    324 /*	The argument specifies an alternative for the timed_read(3) function,
    325 /*	for example, a read function that performs decryption.
    326 /*	This function receives as arguments a file descriptor, buffer pointer,
    327 /*	buffer length, timeout value, and the VSTREAM's context value.
    328 /*	A timeout value <= 0 disables the time limit.
    329 /*	This function should return the positive number of bytes transferred,
    330 /*	0 upon EOF, and -1 upon error with errno set appropriately.
    331 /* .IP "CA_VSTREAM_CTL_WRITE_FN(ssize_t (*)(int, void *, size_t, int, void *))"
    332 /*	The argument specifies an alternative for the timed_write(3) function,
    333 /*	for example, a write function that performs encryption.
    334 /*	This function receives as arguments a file descriptor, buffer pointer,
    335 /*	buffer length, timeout value, and the VSTREAM's context value.
    336 /*	A timeout value <= 0 disables the time limit.
    337 /*	This function should return the positive number of bytes transferred,
    338 /*	and -1 upon error with errno set appropriately. Instead of -1 it may
    339 /*	also return 0, e.g., upon remote party-initiated protocol shutdown.
    340 /* .IP "CA_VSTREAM_CTL_CONTEXT(void *)"
    341 /*	The argument specifies application context that is passed on to
    342 /*	the application-specified read/write routines. No copy is made.
    343 /* .IP "CA_VSTREAM_CTL_PATH(const char *)"
    344 /*	Updates the stored pathname of the specified stream. The pathname
    345 /*	is copied.
    346 /* .IP "CA_VSTREAM_CTL_DOUBLE (no arguments)"
    347 /*	Use separate buffers for reading and for writing.  This prevents
    348 /*	unread input from being discarded upon change of I/O direction.
    349 /* .IP "CA_VSTREAM_CTL_READ_FD(int)"
    350 /*	The argument specifies the file descriptor to be used for reading.
    351 /*	This feature is limited to double-buffered streams, and makes the
    352 /*	stream non-seekable.
    353 /* .IP "CA_VSTREAM_CTL_WRITE_FD(int)"
    354 /*	The argument specifies the file descriptor to be used for writing.
    355 /*	This feature is limited to double-buffered streams, and makes the
    356 /*	stream non-seekable.
    357 /* .IP "CA_VSTREAM_CTL_SWAP_FD(VSTREAM *)"
    358 /*	The argument specifies a VSTREAM pointer; the request swaps the
    359 /*	file descriptor members of the two streams. This feature is limited
    360 /*	to streams that are both double-buffered or both single-buffered.
    361 /* .IP "CA_VSTREAM_CTL_DUPFD(int)"
    362 /*	The argument specifies a minimum file descriptor value. If
    363 /*	the actual stream's file descriptors are below the minimum,
    364 /*	reallocate the descriptors to the first free value greater
    365 /*	than or equal to the minimum. The VSTREAM_CTL_DUPFD macro
    366 /*	is defined only on systems with fcntl() F_DUPFD support.
    367 /* .IP "CA_VSTREAM_CTL_WAITPID_FN(int (*)(pid_t, WAIT_STATUS_T *, int))"
    368 /*	A pointer to function that behaves like waitpid(). This information
    369 /*	is used by the vstream_pclose() routine.
    370 /* .IP "CA_VSTREAM_CTL_TIMEOUT(int)"
    371 /*	The deadline for a descriptor to become readable in case of a read
    372 /*	request, or writable in case of a write request. Specify a value
    373 /*	of 0 to disable deadlines.
    374 /* .IP "CA_VSTREAM_CTL_EXCEPT (no arguments)"
    375 /*	Enable exception handling with vstream_setjmp() and vstream_longjmp().
    376 /*	This involves allocation of additional memory that normally isn't
    377 /*	used.
    378 /* .IP "CA_VSTREAM_CTL_BUFSIZE(ssize_t)"
    379 /*	Specify a non-default buffer size for the next read(2) or
    380 /*	write(2) operation, or zero to implement a no-op. Requests
    381 /*	to reduce the buffer size are silently ignored (i.e. any
    382 /*	positive value <= vstream_req_bufsize()). To get a buffer
    383 /*	size smaller than VSTREAM_BUFSIZE, make the VSTREAM_CTL_BUFSIZE
    384 /*	request before the first stream read or write operation
    385 /*	(i.e., vstream_req_bufsize() returns zero).  Requests to
    386 /*	change a fixed-size buffer (i.e., VSTREAM_ERR) are not
    387 /*	allowed.
    388 /*
    389 /*	NOTE: the vstream_*printf() routines may silently expand a
    390 /*	buffer, so that the result of some %letter specifiers can
    391 /*	be written to contiguous memory.
    392 /* .IP CA_VSTREAM_CTL_START_DEADLINE (no arguments)
    393 /*	Change the VSTREAM_CTL_TIMEOUT behavior, to a deadline for
    394 /*	the total amount of time for all subsequent file descriptor
    395 /*	read or write operations, and recharge the deadline timer.
    396 /* .IP CA_VSTREAM_CTL_STOP_DEADLINE (no arguments)
    397 /*	Revert VSTREAM_CTL_TIMEOUT behavior to the default, i.e.
    398 /*	a time limit for individual file descriptor read or write
    399 /*	operations.
    400 /* .IP CA_VSTREAM_CTL_MIN_DATA_RATE (int)
    401 /*	When the DEADLINE is enabled, the amount of data that must
    402 /*	be transferred to add 1 second to the deadline. However,
    403 /*	the deadline will never exceed the timeout specified with
    404 /*	VSTREAM_CTL_TIMEOUT. A zero value requests no update to the
    405 /*	deadline as data is transferred; that is appropriate for
    406 /*	request/reply interactions.
    407 /* .IP CA_VSTREAM_CTL_OWN_VSTRING (no arguments)
    408 /*	Transfer ownership of the VSTRING that was opened with
    409 /*	vstream_memopen() etc. to the stream, so that the VSTRING
    410 /*	is automatically destroyed when the stream is closed.
    411 /* .PP
    412 /*	vstream_fileno() gives access to the file handle associated with
    413 /*	a buffered stream. With streams that have separate read/write
    414 /*	file descriptors, the result is the current descriptor.
    415 /*
    416 /*	vstream_req_bufsize() returns the buffer size that will be
    417 /*	used for the next read(2) or write(2) operation on the named
    418 /*	stream. A zero result means that the next read(2) or write(2)
    419 /*	operation will use the default buffer size (VSTREAM_BUFSIZE).
    420 /*
    421 /*	vstream_context() returns the application context that is passed on to
    422 /*	the application-specified read/write routines.
    423 /*
    424 /*	VSTREAM_PATH() is an unsafe macro that returns the name stored
    425 /*	with vstream_fopen() or with vstream_control(). The macro is
    426 /*	unsafe because it evaluates some arguments more than once.
    427 /*
    428 /*	vstream_feof() returns non-zero when a previous operation on the
    429 /*	specified stream caused an end-of-file condition.
    430 /*	Although further read requests after EOF may complete
    431 /*	successfully, vstream_feof() will keep returning non-zero
    432 /*	until vstream_clearerr() is called for that stream.
    433 /*
    434 /*	vstream_ferror() returns non-zero when a previous operation on the
    435 /*	specified stream caused a non-EOF error condition, including timeout.
    436 /*	After a non-EOF error on a stream, no I/O request will
    437 /*	complete until after vstream_clearerr() is called for that stream.
    438 /*
    439 /*	vstream_ftimeout() returns non-zero when a previous operation on the
    440 /*	specified stream caused a timeout error condition. See
    441 /*	vstream_ferror() for error persistence details.
    442 /*
    443 /*	vstream_clearerr() resets the timeout, error and end-of-file indication
    444 /*	of the specified stream, and returns no useful result.
    445 /*
    446 /*	vstream_vfprintf() provides an alternate interface
    447 /*	for formatting an argument list according to a format string.
    448 /*
    449 /*	vstream_vprintf() provides a similar alternative interface.
    450 /*
    451 /*	vstream_bufstat() provides input and output buffer status
    452 /*	information.  The command is one of the following:
    453 /* .IP VSTREAM_BST_IN_PEND
    454 /*	Return the number of characters that can be read without
    455 /*	refilling the read buffer.
    456 /* .IP VSTREAM_BST_OUT_PEND
    457 /*	Return the number of characters that are waiting in the
    458 /*	write buffer.
    459 /* .PP
    460 /*	vstream_peek() returns the number of characters that can be
    461 /*	read from the named stream without refilling the read buffer.
    462 /*	This is an alias for vstream_bufstat(stream, VSTREAM_BST_IN_PEND).
    463 /*
    464 /*	vstream_peek_data() returns a pointer to the unread bytes
    465 /*	that exist according to vstream_peek(), or null if no unread
    466 /*	bytes are available.
    467 /*
    468 /*	vstream_setjmp() saves processing context and makes that context
    469 /*	available for use with vstream_longjmp().  Normally, vstream_setjmp()
    470 /*	returns zero.  A non-zero result means that vstream_setjmp() returned
    471 /*	through a vstream_longjmp() call; the result is the \fIval\fR argument
    472 /*	given to vstream_longjmp().
    473 /*
    474 /*	NB: non-local jumps such as vstream_longjmp() are not safe
    475 /*	for jumping out of any routine that manipulates VSTREAM data.
    476 /*	longjmp() like calls are best avoided in signal handlers.
    477 /*
    478 /*	vstream_ftime() returns the time of initialization, the last buffer
    479 /*	fill operation, or the last buffer flush operation for the specified
    480 /*	stream. This information is maintained only when stream timeouts are
    481 /*	enabled.
    482 /*
    483 /*	vstream_ftimeval() is like vstream_ftime() but returns more
    484 /*	detail.
    485 /*
    486 /*	vstream_rd_mumble() and vstream_wr_mumble() report on
    487 /*	read and write error conditions, respectively.
    488 /*
    489 /*	vstream_fstat() queries stream status information about
    490 /*	user-requested features. The \fIflags\fR argument is the
    491 /*	bitwise OR of one or more of the following, and the result
    492 /*	value is the bitwise OR of the features that are activated.
    493 /* .IP VSTREAM_FLAG_DEADLINE
    494 /*	The deadline feature is activated.
    495 /* .IP VSTREAM_FLAG_DOUBLE
    496 /*	The double-buffering feature is activated.
    497 /* .IP VSTREAM_FLAG_MEMORY
    498 /*	The stream is connected to a VSTRING buffer.
    499 /* .IP VSTREAM_FLAG_OWN_VSTRING
    500 /*	The stream 'owns' the VSTRING buffer, and is responsible
    501 /*	for cleaning up when the stream is closed.
    502 /*
    503 /*	vstream_no_debug() disables 'spontaneous' logging of output
    504 /*	activity on the last specified VSTREAM, to prevent recursive
    505 /*	logging.
    506 /* DIAGNOSTICS
    507 /*	Panics: interface violations. Fatal errors: out of memory.
    508 /* SEE ALSO
    509 /*	timed_read(3) default read routine
    510 /*	timed_write(3) default write routine
    511 /*	vbuf_print(3) formatting engine
    512 /*	setjmp(3) non-local jumps
    513 /* BUGS
    514 /*	Should use mmap() on reasonable systems.
    515 /* LICENSE
    516 /* .ad
    517 /* .fi
    518 /*	The Secure Mailer license must be distributed with this software.
    519 /* AUTHOR(S)
    520 /*	Wietse Venema
    521 /*	IBM T.J. Watson Research
    522 /*	P.O. Box 704
    523 /*	Yorktown Heights, NY 10598, USA
    524 /*
    525 /*	Wietse Venema
    526 /*	Google, Inc.
    527 /*	111 8th Avenue
    528 /*	New York, NY 10011, USA
    529 /*--*/
    530 
    531 /* System library. */
    532 
    533 #include <sys_defs.h>
    534 #include <sys/stat.h>
    535 #include <stdlib.h>			/* 44BSD stdarg.h uses abort() */
    536 #include <stdarg.h>
    537 #include <stddef.h>
    538 #include <unistd.h>
    539 #include <fcntl.h>
    540 #include <time.h>
    541 #include <errno.h>
    542 #include <string.h>
    543 
    544 /* Utility library. */
    545 
    546 #define VSTRING_INTERNAL
    547 
    548 #include "mymalloc.h"
    549 #include "msg.h"
    550 #include "vbuf_print.h"
    551 #include "iostuff.h"
    552 #include "vstring.h"
    553 #include "vstream.h"
    554 
    555 /* Application-specific. */
    556 
    557  /*
    558   * Forward declarations.
    559   */
    560 static int vstream_buf_get_ready(VBUF *);
    561 static int vstream_buf_put_ready(VBUF *);
    562 static int vstream_buf_space(VBUF *, ssize_t);
    563 
    564  /*
    565   * Initialization of the three pre-defined streams. Pre-allocate a static
    566   * I/O buffer for the standard error stream, so that the error handler can
    567   * produce a diagnostic even when memory allocation fails.
    568   */
    569 static unsigned char vstream_fstd_buf[VSTREAM_BUFSIZE];
    570 
    571 VSTREAM vstream_fstd[] = {
    572     {{
    573 	    0,				/* flags */
    574 	    0, 0, 0, 0,			/* buffer */
    575 	    vstream_buf_get_ready, vstream_buf_put_ready, vstream_buf_space,
    576     }, STDIN_FILENO, (VSTREAM_RW_FN) timed_read, (VSTREAM_RW_FN) timed_write,
    577     0,},
    578     {{
    579 	    0,				/* flags */
    580 	    0, 0, 0, 0,			/* buffer */
    581 	    vstream_buf_get_ready, vstream_buf_put_ready, vstream_buf_space,
    582     }, STDOUT_FILENO, (VSTREAM_RW_FN) timed_read, (VSTREAM_RW_FN) timed_write,
    583     0,},
    584     {{
    585 	    VBUF_FLAG_FIXED | VSTREAM_FLAG_WRITE,
    586 	    vstream_fstd_buf, VSTREAM_BUFSIZE, VSTREAM_BUFSIZE, vstream_fstd_buf,
    587 	    vstream_buf_get_ready, vstream_buf_put_ready, vstream_buf_space,
    588     }, STDERR_FILENO, (VSTREAM_RW_FN) timed_read, (VSTREAM_RW_FN) timed_write,
    589     VSTREAM_BUFSIZE,},
    590 };
    591 
    592 #define VSTREAM_STATIC(v) ((v) >= VSTREAM_IN && (v) <= VSTREAM_ERR)
    593 
    594  /*
    595   * A bunch of macros to make some expressions more readable. XXX We're
    596   * assuming that O_RDONLY == 0, O_WRONLY == 1, O_RDWR == 2.
    597   */
    598 #define VSTREAM_ACC_MASK(f)	((f) & (O_APPEND | O_WRONLY | O_RDWR))
    599 
    600 #define VSTREAM_CAN_READ(f)	(VSTREAM_ACC_MASK(f) == O_RDONLY \
    601 				|| VSTREAM_ACC_MASK(f) == O_RDWR)
    602 #define VSTREAM_CAN_WRITE(f)	(VSTREAM_ACC_MASK(f) & O_WRONLY \
    603 				|| VSTREAM_ACC_MASK(f) & O_RDWR \
    604 				|| VSTREAM_ACC_MASK(f) & O_APPEND)
    605 
    606 #define VSTREAM_BUF_COUNT(bp, n) \
    607 	((bp)->flags & VSTREAM_FLAG_READ ? -(n) : (n))
    608 
    609 #define VSTREAM_BUF_AT_START(bp) { \
    610 	(bp)->cnt = VSTREAM_BUF_COUNT((bp), (bp)->len); \
    611 	(bp)->ptr = (bp)->data; \
    612     }
    613 
    614 #define VSTREAM_BUF_AT_OFFSET(bp, offset) { \
    615 	(bp)->ptr = (bp)->data + (offset); \
    616 	(bp)->cnt = VSTREAM_BUF_COUNT(bp, (bp)->len - (offset)); \
    617     }
    618 
    619 #define VSTREAM_BUF_AT_END(bp) { \
    620 	(bp)->cnt = 0; \
    621 	(bp)->ptr = (bp)->data + (bp)->len; \
    622     }
    623 
    624 #define VSTREAM_BUF_ZERO(bp) { \
    625 	(bp)->flags = 0; \
    626 	(bp)->data = (bp)->ptr = 0; \
    627 	(bp)->len = (bp)->cnt = 0; \
    628     }
    629 
    630 #define VSTREAM_BUF_ACTIONS(bp, get_action, put_action, space_action) { \
    631 	(bp)->get_ready = (get_action); \
    632 	(bp)->put_ready = (put_action); \
    633 	(bp)->space = (space_action); \
    634     }
    635 
    636 #define VSTREAM_SAVE_STATE(stream, buffer, filedes) { \
    637 	stream->buffer = stream->buf; \
    638 	stream->filedes = stream->fd; \
    639     }
    640 
    641 #define VSTREAM_RESTORE_STATE(stream, buffer, filedes) do { \
    642 	stream->buffer.flags = stream->buf.flags; \
    643 	stream->buf = stream->buffer; \
    644 	stream->fd = stream->filedes; \
    645     } while(0)
    646 
    647 #define VSTREAM_FORK_STATE(stream, buffer, filedes) { \
    648 	stream->buffer = stream->buf; \
    649 	stream->filedes = stream->fd; \
    650 	stream->buffer.data = stream->buffer.ptr = 0; \
    651 	stream->buffer.len = stream->buffer.cnt = 0; \
    652 	stream->buffer.flags &= ~VSTREAM_FLAG_FIXED; \
    653     };
    654 
    655 #define VSTREAM_FLAG_READ_DOUBLE (VSTREAM_FLAG_READ | VSTREAM_FLAG_DOUBLE)
    656 #define VSTREAM_FLAG_WRITE_DOUBLE (VSTREAM_FLAG_WRITE | VSTREAM_FLAG_DOUBLE)
    657 
    658 #define VSTREAM_FFLUSH_SOME(stream) \
    659 	vstream_fflush_some((stream), (stream)->buf.len - (stream)->buf.cnt)
    660 
    661 /* Note: this does not change a negative result into a zero result. */
    662 #define VSTREAM_SUB_TIME(x, y, z) \
    663     do { \
    664 	(x).tv_sec = (y).tv_sec - (z).tv_sec; \
    665 	(x).tv_usec = (y).tv_usec - (z).tv_usec; \
    666 	while ((x).tv_usec < 0) { \
    667 	    (x).tv_usec += 1000000; \
    668 	    (x).tv_sec -= 1; \
    669 	} \
    670 	while ((x).tv_usec >= 1000000) { \
    671 	    (x).tv_usec -= 1000000; \
    672 	    (x).tv_sec += 1; \
    673 	} \
    674     } while (0)
    675 
    676 #define VSTREAM_ADD_TIME(x, y, z) \
    677     do { \
    678 	(x).tv_sec = (y).tv_sec + (z).tv_sec; \
    679 	(x).tv_usec = (y).tv_usec + (z).tv_usec; \
    680 	while ((x).tv_usec >= 1000000) { \
    681 	    (x).tv_usec -= 1000000; \
    682 	    (x).tv_sec += 1; \
    683 	} \
    684     } while (0)
    685 
    686 static VSTREAM *vstream_log_veto;
    687 
    688 /* vstream_buf_init - initialize buffer */
    689 
    690 static void vstream_buf_init(VBUF *bp, int flags)
    691 {
    692 
    693     /*
    694      * Initialize the buffer such that the first data access triggers a
    695      * buffer boundary action.
    696      */
    697     VSTREAM_BUF_ZERO(bp);
    698     VSTREAM_BUF_ACTIONS(bp,
    699 			VSTREAM_CAN_READ(flags) ? vstream_buf_get_ready : 0,
    700 			VSTREAM_CAN_WRITE(flags) ? vstream_buf_put_ready : 0,
    701 			vstream_buf_space);
    702 }
    703 
    704 /* vstream_buf_alloc - allocate buffer memory */
    705 
    706 static void vstream_buf_alloc(VBUF *bp, ssize_t len)
    707 {
    708     VSTREAM *stream = VBUF_TO_APPL(bp, VSTREAM, buf);
    709     ssize_t used = bp->ptr - bp->data;
    710     const char *myname = "vstream_buf_alloc";
    711 
    712     if (len < bp->len)
    713 	msg_panic("%s: attempt to shrink buffer", myname);
    714     if (bp->flags & VSTREAM_FLAG_FIXED)
    715 	msg_panic("%s: unable to extend fixed-size buffer", myname);
    716 
    717     /*
    718      * Late buffer allocation allows the user to override the default policy.
    719      * If a buffer already exists, allow for the presence of (output) data.
    720      */
    721     bp->data = (unsigned char *)
    722 	(bp->data ? myrealloc((void *) bp->data, len) : mymalloc(len));
    723     if (bp->flags & VSTREAM_FLAG_MEMORY)
    724 	memset(bp->data + bp->len, 0, len - bp->len);
    725     bp->len = len;
    726     if (bp->flags & VSTREAM_FLAG_READ) {
    727 	bp->ptr = bp->data + used;
    728 	if (bp->flags & VSTREAM_FLAG_DOUBLE)
    729 	    VSTREAM_SAVE_STATE(stream, read_buf, read_fd);
    730     } else {
    731 	VSTREAM_BUF_AT_OFFSET(bp, used);
    732 	if (bp->flags & VSTREAM_FLAG_DOUBLE)
    733 	    VSTREAM_SAVE_STATE(stream, write_buf, write_fd);
    734     }
    735 }
    736 
    737 /* vstream_buf_wipe - reset buffer to initial state */
    738 
    739 static void vstream_buf_wipe(VBUF *bp)
    740 {
    741     if ((bp->flags & VBUF_FLAG_FIXED) == 0 && bp->data)
    742 	myfree((void *) bp->data);
    743     VSTREAM_BUF_ZERO(bp);
    744     VSTREAM_BUF_ACTIONS(bp, 0, 0, 0);
    745 }
    746 
    747 /* vstream_fflush_some - flush some buffered data */
    748 
    749 static int vstream_fflush_some(VSTREAM *stream, ssize_t to_flush)
    750 {
    751     const char *myname = "vstream_fflush_some";
    752     VBUF   *bp = &stream->buf;
    753     ssize_t used;
    754     ssize_t left_over;
    755     void   *data;
    756     ssize_t len;
    757     ssize_t n;
    758     int     timeout;
    759     struct timeval before;
    760     struct timeval elapsed;
    761     struct timeval bonus;
    762 
    763     /*
    764      * Sanity checks. It is illegal to flush a read-only stream. Otherwise,
    765      * if there is buffered input, discard the input. If there is buffered
    766      * output, require that the amount to flush is larger than the amount to
    767      * keep, so that we can memcpy() the residue.
    768      */
    769     if (bp->put_ready == 0)
    770 	msg_panic("%s: read-only stream", myname);
    771     switch (bp->flags & (VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ)) {
    772     case VSTREAM_FLAG_READ:			/* discard input */
    773 	VSTREAM_BUF_AT_END(bp);
    774 	/* FALLTHROUGH */
    775     case 0:					/* flush after seek? */
    776 	return ((bp->flags & VSTREAM_FLAG_ERR) ? VSTREAM_EOF : 0);
    777     case VSTREAM_FLAG_WRITE:			/* output buffered */
    778 	break;
    779     case VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ:
    780 	msg_panic("%s: read/write stream", myname);
    781     }
    782     used = bp->len - bp->cnt;
    783     left_over = used - to_flush;
    784 
    785     if (msg_verbose > 2 && stream != vstream_log_veto)
    786 	msg_info("%s: fd %d flush %ld", myname, stream->fd, (long) to_flush);
    787     if (to_flush < 0 || left_over < 0)
    788 	msg_panic("%s: bad to_flush %ld", myname, (long) to_flush);
    789     if (to_flush < left_over)
    790 	msg_panic("%s: to_flush < left_over", myname);
    791     if (to_flush == 0)
    792 	return ((bp->flags & VSTREAM_FLAG_ERR) ? VSTREAM_EOF : 0);
    793     if (bp->flags & VSTREAM_FLAG_ERR)
    794 	return (VSTREAM_EOF);
    795 
    796     /*
    797      * When flushing a buffer, allow for partial writes. These can happen
    798      * while talking to a network. Update the cached file seek position, if
    799      * any.
    800      *
    801      * When deadlines are enabled, we count the elapsed time for each write
    802      * operation instead of simply comparing the time-of-day clock with a
    803      * per-stream deadline. The latter could result in anomalies when an
    804      * application does lengthy processing between write operations. Keep in
    805      * mind that a receiver may not be able to keep up when a sender suddenly
    806      * floods it with a lot of data as it tries to catch up with a deadline.
    807      */
    808     for (data = (void *) bp->data, len = to_flush; len > 0; len -= n, data += n) {
    809 	if (bp->flags & VSTREAM_FLAG_DEADLINE) {
    810 	    timeout = stream->time_limit.tv_sec + (stream->time_limit.tv_usec > 0);
    811 	    if (timeout <= 0) {
    812 		bp->flags |= (VSTREAM_FLAG_WR_ERR | VSTREAM_FLAG_WR_TIMEOUT);
    813 		errno = ETIMEDOUT;
    814 		return (VSTREAM_EOF);
    815 	    }
    816 	    if (len == to_flush)
    817 		GETTIMEOFDAY(&before);
    818 	    else
    819 		before = stream->iotime;
    820 	} else
    821 	    timeout = stream->timeout;
    822 	if ((n = stream->write_fn(stream->fd, data, len, timeout, stream->context)) <= 0) {
    823 	    bp->flags |= VSTREAM_FLAG_WR_ERR;
    824 	    if (errno == ETIMEDOUT) {
    825 		bp->flags |= VSTREAM_FLAG_WR_TIMEOUT;
    826 		stream->time_limit.tv_sec = stream->time_limit.tv_usec = 0;
    827 	    }
    828 	    return (VSTREAM_EOF);
    829 	}
    830 	if (timeout) {
    831 	    GETTIMEOFDAY(&stream->iotime);
    832 	    if (bp->flags & VSTREAM_FLAG_DEADLINE) {
    833 		VSTREAM_SUB_TIME(elapsed, stream->iotime, before);
    834 		VSTREAM_SUB_TIME(stream->time_limit, stream->time_limit, elapsed);
    835 		if (stream->min_data_rate > 0) {
    836 		    bonus.tv_sec = n / stream->min_data_rate;
    837 		    bonus.tv_usec = (n % stream->min_data_rate) * 1000000;
    838 		    bonus.tv_usec /= stream->min_data_rate;
    839 		    VSTREAM_ADD_TIME(stream->time_limit, stream->time_limit,
    840 				     bonus);
    841 		    if (stream->time_limit.tv_sec >= stream->timeout) {
    842 			stream->time_limit.tv_sec = stream->timeout;
    843 			stream->time_limit.tv_usec = 0;
    844 		    }
    845 		}
    846 	    }
    847 	}
    848 	if (msg_verbose > 2 && stream != vstream_log_veto && n != to_flush)
    849 	    msg_info("%s: %d flushed %ld/%ld", myname, stream->fd,
    850 		     (long) n, (long) to_flush);
    851     }
    852     if (bp->flags & VSTREAM_FLAG_SEEK)
    853 	stream->offset += to_flush;
    854 
    855     /*
    856      * Allow for partial buffer flush requests. We use memcpy() for reasons
    857      * of portability to pre-ANSI environments (SunOS 4.x or Ultrix 4.x :-).
    858      * This is OK because we have already verified that the to_flush count is
    859      * larger than the left_over count.
    860      */
    861     if (left_over > 0)
    862 	memcpy(bp->data, bp->data + to_flush, left_over);
    863     bp->cnt += to_flush;
    864     bp->ptr -= to_flush;
    865     return ((bp->flags & VSTREAM_FLAG_ERR) ? VSTREAM_EOF : 0);
    866 }
    867 
    868 /* vstream_fflush_delayed - delayed stream flush for double-buffered stream */
    869 
    870 static int vstream_fflush_delayed(VSTREAM *stream)
    871 {
    872     int     status;
    873 
    874     /*
    875      * Sanity check.
    876      */
    877     if ((stream->buf.flags & VSTREAM_FLAG_READ_DOUBLE) != VSTREAM_FLAG_READ_DOUBLE)
    878 	msg_panic("vstream_fflush_delayed: bad flags");
    879 
    880     /*
    881      * Temporarily swap buffers and flush unwritten data. This may seem like
    882      * a lot of work, but it's peanuts compared to the write(2) call that we
    883      * already have avoided. For example, delayed flush is never used on a
    884      * non-pipelined SMTP connection.
    885      */
    886     stream->buf.flags &= ~VSTREAM_FLAG_READ;
    887     VSTREAM_SAVE_STATE(stream, read_buf, read_fd);
    888     stream->buf.flags |= VSTREAM_FLAG_WRITE;
    889     VSTREAM_RESTORE_STATE(stream, write_buf, write_fd);
    890 
    891     status = VSTREAM_FFLUSH_SOME(stream);
    892 
    893     stream->buf.flags &= ~VSTREAM_FLAG_WRITE;
    894     VSTREAM_SAVE_STATE(stream, write_buf, write_fd);
    895     stream->buf.flags |= VSTREAM_FLAG_READ;
    896     VSTREAM_RESTORE_STATE(stream, read_buf, read_fd);
    897 
    898     return (status);
    899 }
    900 
    901 /* vstream_buf_get_ready - vbuf callback to make buffer ready for reading */
    902 
    903 static int vstream_buf_get_ready(VBUF *bp)
    904 {
    905     VSTREAM *stream = VBUF_TO_APPL(bp, VSTREAM, buf);
    906     const char *myname = "vstream_buf_get_ready";
    907     ssize_t n;
    908     struct timeval before;
    909     struct timeval elapsed;
    910     struct timeval bonus;
    911     int     timeout;
    912 
    913     /*
    914      * Detect a change of I/O direction or position. If so, flush any
    915      * unwritten output immediately when the stream is single-buffered, or
    916      * when the stream is double-buffered and the read buffer is empty.
    917      */
    918     switch (bp->flags & (VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ)) {
    919     case VSTREAM_FLAG_WRITE:			/* change direction */
    920 	if (bp->ptr > bp->data)
    921 	    if ((bp->flags & VSTREAM_FLAG_DOUBLE) == 0
    922 		|| stream->read_buf.cnt >= 0)
    923 		if (VSTREAM_FFLUSH_SOME(stream))
    924 		    return (VSTREAM_EOF);
    925 	bp->flags &= ~VSTREAM_FLAG_WRITE;
    926 	if (bp->flags & VSTREAM_FLAG_DOUBLE)
    927 	    VSTREAM_SAVE_STATE(stream, write_buf, write_fd);
    928 	/* FALLTHROUGH */
    929     case 0:					/* change position */
    930 	bp->flags |= VSTREAM_FLAG_READ;
    931 	if (bp->flags & VSTREAM_FLAG_DOUBLE) {
    932 	    VSTREAM_RESTORE_STATE(stream, read_buf, read_fd);
    933 	    if (bp->cnt < 0)
    934 		return (0);
    935 	}
    936 	/* FALLTHROUGH */
    937     case VSTREAM_FLAG_READ:			/* no change */
    938 	break;
    939     case VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ:
    940 	msg_panic("%s: read/write stream", myname);
    941     }
    942 
    943     /*
    944      * If this is the first GET operation, allocate a buffer. Late buffer
    945      * allocation gives the application a chance to override the default
    946      * buffering policy.
    947      *
    948      * XXX Subtle code to set the preferred buffer size as late as possible.
    949      */
    950     if (stream->req_bufsize == 0)
    951 	stream->req_bufsize = VSTREAM_BUFSIZE;
    952     if (bp->len < stream->req_bufsize)
    953 	vstream_buf_alloc(bp, stream->req_bufsize);
    954 
    955     /*
    956      * If the stream is double-buffered and the write buffer is not empty,
    957      * this is the time to flush the write buffer. Delayed flushes reduce
    958      * system call overhead, and on TCP sockets, avoid triggering Nagle's
    959      * algorithm.
    960      */
    961     if ((bp->flags & VSTREAM_FLAG_DOUBLE)
    962 	&& stream->write_buf.len > stream->write_buf.cnt)
    963 	if (vstream_fflush_delayed(stream))
    964 	    return (VSTREAM_EOF);
    965 
    966     /*
    967      * Did we receive an EOF indication?
    968      */
    969     if (bp->flags & VSTREAM_FLAG_EOF)
    970 	return (VSTREAM_EOF);
    971 
    972     /*
    973      * Fill the buffer with as much data as we can handle, or with as much
    974      * data as is available right now, whichever is less. Update the cached
    975      * file seek position, if any.
    976      *
    977      * When deadlines are enabled, we count the elapsed time for each read
    978      * operation instead of simply comparing the time-of-day clock with a
    979      * per-stream deadline. The latter could result in anomalies when an
    980      * application does lengthy processing between read operations. Keep in
    981      * mind that a sender may get blocked, and may not be able to keep up
    982      * when a receiver suddenly wants to read a lot of data as it tries to
    983      * catch up with a deadline.
    984      */
    985     if (bp->flags & VSTREAM_FLAG_DEADLINE) {
    986 	timeout = stream->time_limit.tv_sec + (stream->time_limit.tv_usec > 0);
    987 	if (timeout <= 0) {
    988 	    bp->flags |= (VSTREAM_FLAG_RD_ERR | VSTREAM_FLAG_RD_TIMEOUT);
    989 	    errno = ETIMEDOUT;
    990 	    return (VSTREAM_EOF);
    991 	}
    992 	GETTIMEOFDAY(&before);
    993     } else
    994 	timeout = stream->timeout;
    995     switch (n = stream->read_fn(stream->fd, bp->data, bp->len, timeout, stream->context)) {
    996     case -1:
    997 	bp->flags |= VSTREAM_FLAG_RD_ERR;
    998 	if (errno == ETIMEDOUT) {
    999 	    bp->flags |= VSTREAM_FLAG_RD_TIMEOUT;
   1000 	    stream->time_limit.tv_sec = stream->time_limit.tv_usec = 0;
   1001 	}
   1002 	return (VSTREAM_EOF);
   1003     case 0:
   1004 	bp->flags |= VSTREAM_FLAG_EOF;
   1005 	return (VSTREAM_EOF);
   1006     default:
   1007 	if (timeout) {
   1008 	    GETTIMEOFDAY(&stream->iotime);
   1009 	    if (bp->flags & VSTREAM_FLAG_DEADLINE) {
   1010 		VSTREAM_SUB_TIME(elapsed, stream->iotime, before);
   1011 		VSTREAM_SUB_TIME(stream->time_limit, stream->time_limit, elapsed);
   1012 		if (stream->min_data_rate > 0) {
   1013 		    bonus.tv_sec = n / stream->min_data_rate;
   1014 		    bonus.tv_usec = (n % stream->min_data_rate) * 1000000;
   1015 		    bonus.tv_usec /= stream->min_data_rate;
   1016 		    VSTREAM_ADD_TIME(stream->time_limit, stream->time_limit,
   1017 				     bonus);
   1018 		    if (stream->time_limit.tv_sec >= stream->timeout) {
   1019 			stream->time_limit.tv_sec = stream->timeout;
   1020 			stream->time_limit.tv_usec = 0;
   1021 		    }
   1022 		}
   1023 	    }
   1024 	}
   1025 	if (msg_verbose > 2)
   1026 	    msg_info("%s: fd %d got %ld", myname, stream->fd, (long) n);
   1027 	bp->cnt = -n;
   1028 	bp->ptr = bp->data;
   1029 	if (bp->flags & VSTREAM_FLAG_SEEK)
   1030 	    stream->offset += n;
   1031 	return (0);
   1032     }
   1033 }
   1034 
   1035 /* vstream_buf_put_ready - vbuf callback to make buffer ready for writing */
   1036 
   1037 static int vstream_buf_put_ready(VBUF *bp)
   1038 {
   1039     VSTREAM *stream = VBUF_TO_APPL(bp, VSTREAM, buf);
   1040     const char *myname = "vstream_buf_put_ready";
   1041 
   1042     /*
   1043      * Sanity checks. Detect a change of I/O direction or position. If so,
   1044      * discard unread input, and reset the buffer to the beginning.
   1045      */
   1046     switch (bp->flags & (VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ)) {
   1047     case VSTREAM_FLAG_READ:			/* change direction */
   1048 	bp->flags &= ~VSTREAM_FLAG_READ;
   1049 	if (bp->flags & VSTREAM_FLAG_DOUBLE)
   1050 	    VSTREAM_SAVE_STATE(stream, read_buf, read_fd);
   1051 	/* FALLTHROUGH */
   1052     case 0:					/* change position */
   1053 	bp->flags |= VSTREAM_FLAG_WRITE;
   1054 	if (bp->flags & VSTREAM_FLAG_DOUBLE)
   1055 	    VSTREAM_RESTORE_STATE(stream, write_buf, write_fd);
   1056 	else
   1057 	    VSTREAM_BUF_AT_START(bp);
   1058 	/* FALLTHROUGH */
   1059     case VSTREAM_FLAG_WRITE:			/* no change */
   1060 	break;
   1061     case VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ:
   1062 	msg_panic("%s: read/write stream", myname);
   1063     }
   1064 
   1065     /*
   1066      * Remember the direction. If this is the first PUT operation for this
   1067      * stream or if the buffer is smaller than the requested size, allocate a
   1068      * new buffer; obviously there is no data to be flushed yet. Otherwise,
   1069      * flush the buffer.
   1070      *
   1071      * XXX Subtle code to set the preferred buffer size as late as possible.
   1072      */
   1073     if (stream->req_bufsize == 0)
   1074 	stream->req_bufsize = VSTREAM_BUFSIZE;
   1075     if (bp->len < stream->req_bufsize) {
   1076 	vstream_buf_alloc(bp, stream->req_bufsize);
   1077     } else if (bp->cnt <= 0) {
   1078 	if (VSTREAM_FFLUSH_SOME(stream))
   1079 	    return (VSTREAM_EOF);
   1080     }
   1081     return (0);
   1082 }
   1083 
   1084 /* vstream_buf_space - reserve space ahead of time */
   1085 
   1086 static int vstream_buf_space(VBUF *bp, ssize_t want)
   1087 {
   1088     VSTREAM *stream = VBUF_TO_APPL(bp, VSTREAM, buf);
   1089     ssize_t used;
   1090     ssize_t incr;
   1091     ssize_t shortage;
   1092     const char *myname = "vstream_buf_space";
   1093 
   1094     /*
   1095      * Sanity checks. Reserving space implies writing. It is illegal to write
   1096      * to a read-only stream. Detect a change of I/O direction or position.
   1097      * If so, reset the buffer to the beginning.
   1098      */
   1099     if (bp->put_ready == 0)
   1100 	msg_panic("%s: read-only stream", myname);
   1101     if (want < 0)
   1102 	msg_panic("%s: bad length %ld", myname, (long) want);
   1103     switch (bp->flags & (VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE)) {
   1104     case VSTREAM_FLAG_READ:			/* change direction */
   1105 	bp->flags &= ~VSTREAM_FLAG_READ;
   1106 	if (bp->flags & VSTREAM_FLAG_DOUBLE)
   1107 	    VSTREAM_SAVE_STATE(stream, read_buf, read_fd);
   1108 	/* FALLTHROUGH */
   1109     case 0:					/* change position */
   1110 	bp->flags |= VSTREAM_FLAG_WRITE;
   1111 	if (bp->flags & VSTREAM_FLAG_DOUBLE)
   1112 	    VSTREAM_RESTORE_STATE(stream, write_buf, write_fd);
   1113 	else
   1114 	    VSTREAM_BUF_AT_START(bp);
   1115 	/* FALLTHROUGH */
   1116     case VSTREAM_FLAG_WRITE:			/* no change */
   1117 	break;
   1118     case VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE:
   1119 	msg_panic("%s: read/write stream", myname);
   1120     }
   1121 
   1122     /*
   1123      * See if enough space is available. If not, flush a multiple of
   1124      * VSTREAM_BUFSIZE bytes and resize the buffer to a multiple of
   1125      * VSTREAM_BUFSIZE. We flush multiples of VSTREAM_BUFSIZE in an attempt
   1126      * to keep file updates block-aligned for better performance.
   1127      *
   1128      * XXX Subtle code to set the preferred buffer size as late as possible.
   1129      */
   1130 #define VSTREAM_TRUNCATE(count, base)	(((count) / (base)) * (base))
   1131 #define VSTREAM_ROUNDUP(count, base)	VSTREAM_TRUNCATE(count + base - 1, base)
   1132 
   1133     if (stream->req_bufsize == 0)
   1134 	stream->req_bufsize = VSTREAM_BUFSIZE;
   1135     if (want > bp->cnt) {
   1136 	if ((used = bp->len - bp->cnt) > stream->req_bufsize)
   1137 	    if (vstream_fflush_some(stream, VSTREAM_TRUNCATE(used, stream->req_bufsize)))
   1138 		return (VSTREAM_EOF);
   1139 	if ((shortage = (want - bp->cnt)) > 0) {
   1140 	    if ((bp->flags & VSTREAM_FLAG_FIXED)
   1141 		|| shortage > __MAXINT__(ssize_t) -bp->len - stream->req_bufsize) {
   1142 		bp->flags |= VSTREAM_FLAG_WR_ERR;
   1143 	    } else {
   1144 		incr = VSTREAM_ROUNDUP(shortage, stream->req_bufsize);
   1145 		vstream_buf_alloc(bp, bp->len + incr);
   1146 	    }
   1147 	}
   1148     }
   1149     return (vstream_ferror(stream) ? VSTREAM_EOF : 0);	/* mmap() may fail */
   1150 }
   1151 
   1152 /* vstream_fpurge - discard unread or unwritten content */
   1153 
   1154 int     vstream_fpurge(VSTREAM *stream, int direction)
   1155 {
   1156     const char *myname = "vstream_fpurge";
   1157     VBUF   *bp = &stream->buf;
   1158 
   1159 #define VSTREAM_MAYBE_PURGE_WRITE(d, b) if ((d) & VSTREAM_PURGE_WRITE) \
   1160 	VSTREAM_BUF_AT_START((b))
   1161 #define VSTREAM_MAYBE_PURGE_READ(d, b) if ((d) & VSTREAM_PURGE_READ) \
   1162 	VSTREAM_BUF_AT_END((b))
   1163 
   1164     /*
   1165      * To discard all unread contents, position the read buffer at its end,
   1166      * so that we skip over any unread data, and so that the next read
   1167      * operation will refill the buffer.
   1168      *
   1169      * To discard all unwritten content, position the write buffer at its
   1170      * beginning, so that the next write operation clobbers any unwritten
   1171      * data.
   1172      */
   1173     switch (bp->flags & (VSTREAM_FLAG_READ_DOUBLE | VSTREAM_FLAG_WRITE)) {
   1174     case VSTREAM_FLAG_READ_DOUBLE:
   1175 	VSTREAM_MAYBE_PURGE_WRITE(direction, &stream->write_buf);
   1176 	/* FALLTHROUGH */
   1177     case VSTREAM_FLAG_READ:
   1178 	VSTREAM_MAYBE_PURGE_READ(direction, bp);
   1179 	break;
   1180     case VSTREAM_FLAG_DOUBLE:
   1181 	VSTREAM_MAYBE_PURGE_WRITE(direction, &stream->write_buf);
   1182 	VSTREAM_MAYBE_PURGE_READ(direction, &stream->read_buf);
   1183 	break;
   1184     case VSTREAM_FLAG_WRITE_DOUBLE:
   1185 	VSTREAM_MAYBE_PURGE_READ(direction, &stream->read_buf);
   1186 	/* FALLTHROUGH */
   1187     case VSTREAM_FLAG_WRITE:
   1188 	VSTREAM_MAYBE_PURGE_WRITE(direction, bp);
   1189 	break;
   1190     case VSTREAM_FLAG_READ_DOUBLE | VSTREAM_FLAG_WRITE:
   1191     case VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE:
   1192 	msg_panic("%s: read/write stream", myname);
   1193     }
   1194 
   1195     /*
   1196      * Invalidate the cached file seek position.
   1197      */
   1198     bp->flags &= ~VSTREAM_FLAG_SEEK;
   1199     stream->offset = 0;
   1200 
   1201     return (0);
   1202 }
   1203 
   1204 /* vstream_fseek - change I/O position */
   1205 
   1206 off_t   vstream_fseek(VSTREAM *stream, off_t offset, int whence)
   1207 {
   1208     const char *myname = "vstream_fseek";
   1209     VBUF   *bp = &stream->buf;
   1210 
   1211     /*
   1212      * TODO: support data length (data length != buffer length). Without data
   1213      * length information, Without explicit data length information,
   1214      * vstream_memopen(O_RDONLY) has to set the VSTREAM buffer length to the
   1215      * vstring payload length to avoid accessing unwritten data after
   1216      * vstream_fseek(), because for lseek() compatibility, vstream_fseek()
   1217      * must allow seeking past the end of a file.
   1218      */
   1219     if (stream->buf.flags & VSTREAM_FLAG_MEMORY) {
   1220 	if (whence == SEEK_CUR)
   1221 	    offset += (bp->ptr - bp->data);
   1222 	else if (whence == SEEK_END)
   1223 	    offset += bp->len;
   1224 	if (offset < 0) {
   1225 	    errno = EINVAL;
   1226 	    return (-1);
   1227 	}
   1228 	if (offset > bp->len && (bp->flags & VSTREAM_FLAG_WRITE))
   1229 	    vstream_buf_space(bp, offset - bp->len);
   1230 	VSTREAM_BUF_AT_OFFSET(bp, offset);
   1231 	return (offset);
   1232     }
   1233 
   1234     /*
   1235      * Flush any unwritten output. Discard any unread input. Position the
   1236      * buffer at the end, so that the next GET or PUT operation triggers a
   1237      * buffer boundary action.
   1238      */
   1239     switch (bp->flags & (VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE)) {
   1240     case VSTREAM_FLAG_WRITE:
   1241 	if (bp->ptr > bp->data) {
   1242 	    if (whence == SEEK_CUR)
   1243 		offset += (bp->ptr - bp->data);	/* add unwritten data */
   1244 	    else if (whence == SEEK_END)
   1245 		bp->flags &= ~VSTREAM_FLAG_SEEK;
   1246 	    if (VSTREAM_FFLUSH_SOME(stream))
   1247 		return (-1);
   1248 	}
   1249 	VSTREAM_BUF_AT_END(bp);
   1250 	break;
   1251     case VSTREAM_FLAG_READ:
   1252 	if (whence == SEEK_CUR)
   1253 	    offset += bp->cnt;			/* subtract unread data */
   1254 	else if (whence == SEEK_END)
   1255 	    bp->flags &= ~VSTREAM_FLAG_SEEK;
   1256 	/* FALLTHROUGH */
   1257     case 0:
   1258 	VSTREAM_BUF_AT_END(bp);
   1259 	break;
   1260     case VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE:
   1261 	msg_panic("%s: read/write stream", myname);
   1262     }
   1263 
   1264     /*
   1265      * Clear the read/write flags to inform the buffer boundary action
   1266      * routines that we may have changed I/O position.
   1267      */
   1268     bp->flags &= ~(VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE);
   1269 
   1270     /*
   1271      * Shave an unnecessary system call.
   1272      */
   1273     if (bp->flags & VSTREAM_FLAG_NSEEK) {
   1274 	errno = ESPIPE;
   1275 	return (-1);
   1276     }
   1277 
   1278     /*
   1279      * Update the cached file seek position.
   1280      */
   1281     if ((stream->offset = lseek(stream->fd, offset, whence)) < 0) {
   1282 	if (errno == ESPIPE)
   1283 	    bp->flags |= VSTREAM_FLAG_NSEEK;
   1284     } else {
   1285 	bp->flags |= VSTREAM_FLAG_SEEK;
   1286     }
   1287     bp->flags &= ~VSTREAM_FLAG_EOF;
   1288     return (stream->offset);
   1289 }
   1290 
   1291 /* vstream_ftell - return file offset */
   1292 
   1293 off_t   vstream_ftell(VSTREAM *stream)
   1294 {
   1295     VBUF   *bp = &stream->buf;
   1296 
   1297     /*
   1298      * Special case for memory buffer.
   1299      */
   1300     if (stream->buf.flags & VSTREAM_FLAG_MEMORY)
   1301 	return (bp->ptr - bp->data);
   1302 
   1303     /*
   1304      * Shave an unnecessary syscall.
   1305      */
   1306     if (bp->flags & VSTREAM_FLAG_NSEEK) {
   1307 	errno = ESPIPE;
   1308 	return (-1);
   1309     }
   1310 
   1311     /*
   1312      * Use the cached file offset when available. This is the offset after
   1313      * the last read, write or seek operation.
   1314      */
   1315     if ((bp->flags & VSTREAM_FLAG_SEEK) == 0) {
   1316 	if ((stream->offset = lseek(stream->fd, (off_t) 0, SEEK_CUR)) < 0) {
   1317 	    bp->flags |= VSTREAM_FLAG_NSEEK;
   1318 	    return (-1);
   1319 	}
   1320 	bp->flags |= VSTREAM_FLAG_SEEK;
   1321     }
   1322 
   1323     /*
   1324      * If this is a read buffer, subtract the number of unread bytes from the
   1325      * cached offset. Remember that read counts are negative.
   1326      */
   1327     if (bp->flags & VSTREAM_FLAG_READ)
   1328 	return (stream->offset + bp->cnt);
   1329 
   1330     /*
   1331      * If this is a write buffer, add the number of unwritten bytes to the
   1332      * cached offset.
   1333      */
   1334     if (bp->flags & VSTREAM_FLAG_WRITE)
   1335 	return (stream->offset + (bp->ptr - bp->data));
   1336 
   1337     /*
   1338      * Apparently, this is a new buffer, or a buffer after seek, so there is
   1339      * no need to account for unread or unwritten data.
   1340      */
   1341     return (stream->offset);
   1342 }
   1343 
   1344 /* vstream_subopen - initialize everything except buffers and I/O handlers */
   1345 
   1346 static VSTREAM *vstream_subopen(void)
   1347 {
   1348     VSTREAM *stream;
   1349 
   1350     /* Note: memset() is not a portable way to initialize non-integer types. */
   1351     stream = (VSTREAM *) mymalloc(sizeof(*stream));
   1352     stream->offset = 0;
   1353     stream->path = 0;
   1354     stream->pid = 0;
   1355     stream->waitpid_fn = 0;
   1356     stream->timeout = 0;
   1357     stream->context = 0;
   1358     stream->jbuf = 0;
   1359     stream->iotime.tv_sec = stream->iotime.tv_usec = 0;
   1360     stream->time_limit.tv_sec = stream->time_limit.tv_usec = 0;
   1361     stream->req_bufsize = 0;
   1362     stream->vstring = 0;
   1363     stream->min_data_rate = 0;
   1364     return (stream);
   1365 }
   1366 
   1367 /* vstream_fdopen - add buffering to pre-opened stream */
   1368 
   1369 VSTREAM *vstream_fdopen(int fd, int flags)
   1370 {
   1371     VSTREAM *stream;
   1372 
   1373     /*
   1374      * Sanity check.
   1375      */
   1376     if (fd < 0)
   1377 	msg_panic("vstream_fdopen: bad file %d", fd);
   1378 
   1379     /*
   1380      * Initialize buffers etc. but do as little as possible. Late buffer
   1381      * allocation etc. gives the application a chance to override default
   1382      * policies. Either this, or the vstream*open() routines would have to
   1383      * have a really ugly interface with lots of mostly-unused arguments (can
   1384      * you say VMS?).
   1385      */
   1386     stream = vstream_subopen();
   1387     stream->fd = fd;
   1388     stream->read_fn = VSTREAM_CAN_READ(flags) ? (VSTREAM_RW_FN) timed_read : 0;
   1389     stream->write_fn = VSTREAM_CAN_WRITE(flags) ? (VSTREAM_RW_FN) timed_write : 0;
   1390     vstream_buf_init(&stream->buf, flags);
   1391     return (stream);
   1392 }
   1393 
   1394 /* vstream_fopen - open buffered file stream */
   1395 
   1396 VSTREAM *vstream_fopen(const char *path, int flags, mode_t mode)
   1397 {
   1398     VSTREAM *stream;
   1399     int     fd;
   1400 
   1401     /*
   1402      * To set permissions on new files only, we need to distinguish between
   1403      * creating a new file and opening an existing one.
   1404      */
   1405 #define open_create(path, flags, mode) \
   1406 	open((path), (flags) | (O_CREAT | O_EXCL), (mode))
   1407 #define open_exist(path, flags, mode) \
   1408 	open((path), (flags) & ~(O_CREAT | O_EXCL), (mode))
   1409 
   1410     switch (flags & (O_CREAT | O_EXCL)) {
   1411     case O_CREAT:
   1412 	fd = open_exist(path, flags, mode);
   1413 	if (fd < 0 && errno == ENOENT) {
   1414 	    fd = open_create(path, flags, mode);
   1415 	    if (fd >= 0) {
   1416 		if (fchmod(fd, mode) < 0)	/* can't uncreate */
   1417 		    msg_warn("fchmod %s 0%o: %m", path, (unsigned) mode);
   1418 	    } else if ( /* fd < 0 && */ errno == EEXIST)
   1419 		fd = open_exist(path, flags, mode);
   1420 	}
   1421 	break;
   1422     case O_CREAT | O_EXCL:
   1423 	fd = open(path, flags, mode);
   1424 	if (fd >= 0)
   1425 	    if (fchmod(fd, mode) < 0)		/* can't uncreate */
   1426 		msg_warn("fchmod %s 0%o: %m", path, (unsigned) mode);
   1427 	break;
   1428     default:
   1429 	fd = open(path, flags, mode);
   1430 	break;
   1431     }
   1432     if (fd < 0) {
   1433 	return (0);
   1434     } else {
   1435 	stream = vstream_fdopen(fd, flags);
   1436 	stream->path = mystrdup(path);
   1437 	return (stream);
   1438     }
   1439 }
   1440 
   1441 /* vstream_fflush - flush write buffer */
   1442 
   1443 int     vstream_fflush(VSTREAM *stream)
   1444 {
   1445 
   1446     /*
   1447      * With VSTRING, the write pointer must be positioned behind the end of
   1448      * data. But vstream_fseek() changes the write position, and causes the
   1449      * data length to be forgotten. Before flushing to vstream, remember the
   1450      * current write position, move the write pointer and do what needs to be
   1451      * done, then move the write pointer back to the saved location.
   1452      */
   1453     if (stream->buf.flags & VSTREAM_FLAG_MEMORY) {
   1454 	if (stream->buf.flags & VSTREAM_FLAG_WRITE) {
   1455 	    VSTRING *string = stream->vstring;
   1456 
   1457 #ifdef PENDING_VSTREAM_FSEEK_FOR_MEMORY
   1458 	    VSTREAM_BUF_AT_OFFSET(&stream->buf, stream->buf.data_len);
   1459 #endif
   1460 	    memcpy(&string->vbuf, &stream->buf, sizeof(stream->buf));
   1461 	    string->vbuf.flags &= VSTRING_FLAG_MASK;
   1462 	    VSTRING_TERMINATE(string);
   1463 	}
   1464 	return (0);
   1465     }
   1466     if ((stream->buf.flags & VSTREAM_FLAG_READ_DOUBLE)
   1467 	== VSTREAM_FLAG_READ_DOUBLE
   1468 	&& stream->write_buf.len > stream->write_buf.cnt)
   1469 	vstream_fflush_delayed(stream);
   1470     return (VSTREAM_FFLUSH_SOME(stream));
   1471 }
   1472 
   1473 /* vstream_fclose - close buffered stream */
   1474 
   1475 int     vstream_fclose(VSTREAM *stream)
   1476 {
   1477     int     err;
   1478 
   1479     /*
   1480      * NOTE: Negative file descriptors are not part of the external
   1481      * interface. They are for internal use only, in order to support
   1482      * vstream_fdclose() without a lot of code duplication. Applications that
   1483      * rely on negative VSTREAM file descriptors will break without warning.
   1484      */
   1485     if (stream->pid != 0)
   1486 	msg_panic("vstream_fclose: stream has process");
   1487     if ((stream->buf.flags & VSTREAM_FLAG_MEMORY)
   1488 	|| ((stream->buf.flags & VSTREAM_FLAG_WRITE_DOUBLE) != 0
   1489 	    && stream->fd >= 0))
   1490 	vstream_fflush(stream);
   1491     /* Do not remove: vstream_fdclose() depends on this error test. */
   1492     err = vstream_ferror(stream);
   1493     if (stream->buf.flags & VSTREAM_FLAG_DOUBLE) {
   1494 	if (stream->read_fd >= 0)
   1495 	    err |= close(stream->read_fd);
   1496 	if (stream->write_fd != stream->read_fd)
   1497 	    if (stream->write_fd >= 0)
   1498 		err |= close(stream->write_fd);
   1499 	vstream_buf_wipe(&stream->read_buf);
   1500 	vstream_buf_wipe(&stream->write_buf);
   1501 	stream->buf = stream->read_buf;
   1502     } else {
   1503 	if (stream->fd >= 0)
   1504 	    err |= close(stream->fd);
   1505 	if ((stream->buf.flags & VSTREAM_FLAG_MEMORY) == 0)
   1506 	    vstream_buf_wipe(&stream->buf);
   1507     }
   1508     if (stream->path)
   1509 	myfree(stream->path);
   1510     if (stream->jbuf)
   1511 	myfree((void *) stream->jbuf);
   1512     if (stream->vstring && (stream->buf.flags & VSTREAM_FLAG_OWN_VSTRING))
   1513 	vstring_free(stream->vstring);
   1514     if (!VSTREAM_STATIC(stream))
   1515 	myfree((void *) stream);
   1516     return (err ? VSTREAM_EOF : 0);
   1517 }
   1518 
   1519 /* vstream_fdclose - close stream, leave file(s) open */
   1520 
   1521 int     vstream_fdclose(VSTREAM *stream)
   1522 {
   1523 
   1524     /*
   1525      * Flush unwritten output, just like vstream_fclose(). Errors are
   1526      * reported by vstream_fclose().
   1527      */
   1528     if ((stream->buf.flags & VSTREAM_FLAG_WRITE_DOUBLE) != 0)
   1529 	(void) vstream_fflush(stream);
   1530 
   1531     /*
   1532      * NOTE: Negative file descriptors are not part of the external
   1533      * interface. They are for internal use only, in order to support
   1534      * vstream_fdclose() without a lot of code duplication. Applications that
   1535      * rely on negative VSTREAM file descriptors will break without warning.
   1536      */
   1537     if (stream->buf.flags & VSTREAM_FLAG_DOUBLE) {
   1538 	stream->fd = stream->read_fd = stream->write_fd = -1;
   1539     } else {
   1540 	stream->fd = -1;
   1541     }
   1542     return (vstream_fclose(stream));
   1543 }
   1544 
   1545 /* vstream_printf - formatted print to stdout */
   1546 
   1547 VSTREAM *vstream_printf(const char *fmt,...)
   1548 {
   1549     VSTREAM *stream = VSTREAM_OUT;
   1550     va_list ap;
   1551 
   1552     va_start(ap, fmt);
   1553     vbuf_print(&stream->buf, fmt, ap);
   1554     va_end(ap);
   1555     return (stream);
   1556 }
   1557 
   1558 /* vstream_fprintf - formatted print to buffered stream */
   1559 
   1560 VSTREAM *vstream_fprintf(VSTREAM *stream, const char *fmt,...)
   1561 {
   1562     va_list ap;
   1563 
   1564     va_start(ap, fmt);
   1565     vbuf_print(&stream->buf, fmt, ap);
   1566     va_end(ap);
   1567     return (stream);
   1568 }
   1569 
   1570 /* vstream_fputs - write string to stream */
   1571 
   1572 int     vstream_fputs(const char *str, VSTREAM *stream)
   1573 {
   1574     int     ch;
   1575 
   1576     while ((ch = *str++) != 0)
   1577 	if (VSTREAM_PUTC(ch, stream) == VSTREAM_EOF)
   1578 	    return (VSTREAM_EOF);
   1579     return (0);
   1580 }
   1581 
   1582 /* vstream_fread_buf - unformatted read to VSTRING */
   1583 
   1584 ssize_t vstream_fread_buf(VSTREAM *fp, VSTRING *vp, ssize_t len)
   1585 {
   1586     ssize_t ret;
   1587 
   1588     VSTRING_RESET(vp);
   1589     VSTRING_SPACE(vp, len);
   1590     ret = vstream_fread(fp, vstring_str(vp), len);
   1591     if (ret > 0)
   1592 	VSTRING_AT_OFFSET(vp, ret);
   1593     return (ret);
   1594 }
   1595 
   1596 /* vstream_fread_app - unformatted read to VSTRING */
   1597 
   1598 ssize_t vstream_fread_app(VSTREAM *fp, VSTRING *vp, ssize_t len)
   1599 {
   1600     ssize_t ret;
   1601 
   1602     VSTRING_SPACE(vp, len);
   1603     ret = vstream_fread(fp, vstring_end(vp), len);
   1604     if (ret > 0)
   1605 	VSTRING_AT_OFFSET(vp, VSTRING_LEN(vp) + ret);
   1606     return (ret);
   1607 }
   1608 
   1609 /* vstream_control - fine control */
   1610 
   1611 void    vstream_control(VSTREAM *stream, int name,...)
   1612 {
   1613     const char *myname = "vstream_control";
   1614     va_list ap;
   1615     int     floor;
   1616     int     old_fd;
   1617     ssize_t req_bufsize = 0;
   1618     VSTREAM *stream2;
   1619     int     min_data_rate;
   1620 
   1621 #define SWAP(type,a,b) do { type temp = (a); (a) = (b); (b) = (temp); } while (0)
   1622 
   1623     /*
   1624      * A crude 'allow' filter for memory streams.
   1625      */
   1626     int     memory_ops =
   1627     ((1 << VSTREAM_CTL_END) | (1 << VSTREAM_CTL_CONTEXT)
   1628      | (1 << VSTREAM_CTL_PATH) | (1 << VSTREAM_CTL_EXCEPT)
   1629      | (1 << VSTREAM_CTL_OWN_VSTRING));
   1630 
   1631     for (va_start(ap, name); name != VSTREAM_CTL_END; name = va_arg(ap, int)) {
   1632 	if ((stream->buf.flags & VSTREAM_FLAG_MEMORY)
   1633 	    && (memory_ops & (1 << name)) == 0)
   1634 	    msg_panic("%s: memory stream does not support VSTREAM_CTL_%d",
   1635 		      VSTREAM_PATH(stream), name);
   1636 	switch (name) {
   1637 	case VSTREAM_CTL_READ_FN:
   1638 	    stream->read_fn = va_arg(ap, VSTREAM_RW_FN);
   1639 	    break;
   1640 	case VSTREAM_CTL_WRITE_FN:
   1641 	    stream->write_fn = va_arg(ap, VSTREAM_RW_FN);
   1642 	    break;
   1643 	case VSTREAM_CTL_CONTEXT:
   1644 	    stream->context = va_arg(ap, void *);
   1645 	    break;
   1646 	case VSTREAM_CTL_PATH:
   1647 	    if (stream->path)
   1648 		myfree(stream->path);
   1649 	    stream->path = mystrdup(va_arg(ap, char *));
   1650 	    break;
   1651 	case VSTREAM_CTL_DOUBLE:
   1652 	    if ((stream->buf.flags & VSTREAM_FLAG_DOUBLE) == 0) {
   1653 		stream->buf.flags |= VSTREAM_FLAG_DOUBLE;
   1654 		if (stream->buf.flags & VSTREAM_FLAG_READ) {
   1655 		    VSTREAM_SAVE_STATE(stream, read_buf, read_fd);
   1656 		    VSTREAM_FORK_STATE(stream, write_buf, write_fd);
   1657 		} else {
   1658 		    VSTREAM_SAVE_STATE(stream, write_buf, write_fd);
   1659 		    VSTREAM_FORK_STATE(stream, read_buf, read_fd);
   1660 		}
   1661 	    }
   1662 	    break;
   1663 	case VSTREAM_CTL_READ_FD:
   1664 	    if ((stream->buf.flags & VSTREAM_FLAG_DOUBLE) == 0)
   1665 		msg_panic("VSTREAM_CTL_READ_FD requires double buffering");
   1666 	    stream->read_fd = va_arg(ap, int);
   1667 	    stream->buf.flags |= VSTREAM_FLAG_NSEEK;
   1668 	    break;
   1669 	case VSTREAM_CTL_WRITE_FD:
   1670 	    if ((stream->buf.flags & VSTREAM_FLAG_DOUBLE) == 0)
   1671 		msg_panic("VSTREAM_CTL_WRITE_FD requires double buffering");
   1672 	    stream->write_fd = va_arg(ap, int);
   1673 	    stream->buf.flags |= VSTREAM_FLAG_NSEEK;
   1674 	    break;
   1675 	case VSTREAM_CTL_SWAP_FD:
   1676 	    stream2 = va_arg(ap, VSTREAM *);
   1677 	    if ((stream->buf.flags & VSTREAM_FLAG_DOUBLE)
   1678 		!= (stream2->buf.flags & VSTREAM_FLAG_DOUBLE))
   1679 		msg_panic("VSTREAM_CTL_SWAP_FD can't swap descriptors between "
   1680 			  "single-buffered and double-buffered streams");
   1681 	    if (stream->buf.flags & VSTREAM_FLAG_DOUBLE) {
   1682 		SWAP(int, stream->read_fd, stream2->read_fd);
   1683 		SWAP(int, stream->write_fd, stream2->write_fd);
   1684 		stream->fd = ((stream->buf.flags & VSTREAM_FLAG_WRITE) ?
   1685 			      stream->write_fd : stream->read_fd);
   1686 	    } else {
   1687 		SWAP(int, stream->fd, stream2->fd);
   1688 	    }
   1689 	    break;
   1690 	case VSTREAM_CTL_TIMEOUT:
   1691 	    if (stream->timeout == 0)
   1692 		GETTIMEOFDAY(&stream->iotime);
   1693 	    stream->timeout = va_arg(ap, int);
   1694 	    if (stream->timeout < 0)
   1695 		msg_panic("%s: bad timeout %d", myname, stream->timeout);
   1696 	    break;
   1697 	case VSTREAM_CTL_EXCEPT:
   1698 	    if (stream->jbuf == 0)
   1699 		stream->jbuf =
   1700 		    (VSTREAM_JMP_BUF *) mymalloc(sizeof(VSTREAM_JMP_BUF));
   1701 	    break;
   1702 
   1703 #ifdef VSTREAM_CTL_DUPFD
   1704 
   1705 #define VSTREAM_TRY_DUPFD(backup, fd, floor) do { \
   1706 	if (((backup) = (fd)) < floor) { \
   1707 	    if (((fd) = fcntl((backup), F_DUPFD, (floor))) < 0) \
   1708 		msg_fatal("fcntl F_DUPFD %d: %m", (floor)); \
   1709 	    (void) close(backup); \
   1710 	} \
   1711     } while (0)
   1712 
   1713 	case VSTREAM_CTL_DUPFD:
   1714 	    floor = va_arg(ap, int);
   1715 	    if (stream->buf.flags & VSTREAM_FLAG_DOUBLE) {
   1716 		VSTREAM_TRY_DUPFD(old_fd, stream->read_fd, floor);
   1717 		if (stream->write_fd == old_fd)
   1718 		    stream->write_fd = stream->read_fd;
   1719 		else
   1720 		    VSTREAM_TRY_DUPFD(old_fd, stream->write_fd, floor);
   1721 		stream->fd = (stream->buf.flags & VSTREAM_FLAG_READ) ?
   1722 		    stream->read_fd : stream->write_fd;
   1723 	    } else {
   1724 		VSTREAM_TRY_DUPFD(old_fd, stream->fd, floor);
   1725 	    }
   1726 	    break;
   1727 #endif
   1728 
   1729 	    /*
   1730 	     * Postpone memory (re)allocation until the space is needed.
   1731 	     */
   1732 	case VSTREAM_CTL_BUFSIZE:
   1733 	    req_bufsize = va_arg(ap, ssize_t);
   1734 	    /* Heuristic to detect missing (ssize_t) type cast on LP64 hosts. */
   1735 	    if (req_bufsize < 0 || req_bufsize > INT_MAX)
   1736 		msg_panic("unreasonable VSTREAM_CTL_BUFSIZE request: %ld",
   1737 			  (long) req_bufsize);
   1738 	    if ((stream->buf.flags & VSTREAM_FLAG_FIXED) == 0
   1739 		&& req_bufsize > stream->req_bufsize) {
   1740 		if (msg_verbose)
   1741 		    msg_info("fd=%d: stream buffer size old=%ld new=%ld",
   1742 			     vstream_fileno(stream),
   1743 			     (long) stream->req_bufsize,
   1744 			     (long) req_bufsize);
   1745 		stream->req_bufsize = req_bufsize;
   1746 	    }
   1747 	    break;
   1748 
   1749 	    /*
   1750 	     * Make no gettimeofday() etc. system call until we really know
   1751 	     * that we need to do I/O. This avoids a performance hit when
   1752 	     * sending or receiving body content one line at a time.
   1753 	     */
   1754 	case VSTREAM_CTL_STOP_DEADLINE:
   1755 	    stream->buf.flags &= ~VSTREAM_FLAG_DEADLINE;
   1756 	    break;
   1757 	case VSTREAM_CTL_START_DEADLINE:
   1758 	    if (stream->timeout <= 0)
   1759 		msg_panic("%s: bad timeout %d", myname, stream->timeout);
   1760 	    stream->buf.flags |= VSTREAM_FLAG_DEADLINE;
   1761 	    stream->time_limit.tv_sec = stream->timeout;
   1762 	    stream->time_limit.tv_usec = 0;
   1763 	    break;
   1764 	case VSTREAM_CTL_MIN_DATA_RATE:
   1765 	    min_data_rate = va_arg(ap, int);
   1766 	    if (min_data_rate < 0)
   1767 		msg_panic("%s: bad min_data_rate %d", myname, min_data_rate);
   1768 	    stream->min_data_rate = min_data_rate;
   1769 	    break;
   1770 	case VSTREAM_CTL_OWN_VSTRING:
   1771 	    if ((stream->buf.flags |= VSTREAM_FLAG_MEMORY) == 0)
   1772 		msg_panic("%s: operation on non-VSTRING stream", myname);
   1773 	    stream->buf.flags |= VSTREAM_FLAG_OWN_VSTRING;
   1774 	    break;
   1775 	default:
   1776 	    msg_panic("%s: bad name %d", myname, name);
   1777 	}
   1778     }
   1779     va_end(ap);
   1780 }
   1781 
   1782 /* vstream_vprintf - formatted print to stdout */
   1783 
   1784 VSTREAM *vstream_vprintf(const char *format, va_list ap)
   1785 {
   1786     VSTREAM *vp = VSTREAM_OUT;
   1787 
   1788     vbuf_print(&vp->buf, format, ap);
   1789     return (vp);
   1790 }
   1791 
   1792 /* vstream_vfprintf - formatted print engine */
   1793 
   1794 VSTREAM *vstream_vfprintf(VSTREAM *vp, const char *format, va_list ap)
   1795 {
   1796     vbuf_print(&vp->buf, format, ap);
   1797     return (vp);
   1798 }
   1799 
   1800 /* vstream_bufstat - get stream buffer status */
   1801 
   1802 ssize_t vstream_bufstat(VSTREAM *vp, int command)
   1803 {
   1804     VBUF   *bp;
   1805 
   1806     switch (command & VSTREAM_BST_MASK_DIR) {
   1807     case VSTREAM_BST_FLAG_IN:
   1808 	if (vp->buf.flags & VSTREAM_FLAG_READ) {
   1809 	    bp = &vp->buf;
   1810 	} else if (vp->buf.flags & VSTREAM_FLAG_DOUBLE) {
   1811 	    bp = &vp->read_buf;
   1812 	} else {
   1813 	    bp = 0;
   1814 	}
   1815 	switch (command & ~VSTREAM_BST_MASK_DIR) {
   1816 	case VSTREAM_BST_FLAG_PEND:
   1817 	    return (bp ? -bp->cnt : 0);
   1818 	    /* Add other requests below. */
   1819 	}
   1820 	break;
   1821     case VSTREAM_BST_FLAG_OUT:
   1822 	if (vp->buf.flags & VSTREAM_FLAG_WRITE) {
   1823 	    bp = &vp->buf;
   1824 	} else if (vp->buf.flags & VSTREAM_FLAG_DOUBLE) {
   1825 	    bp = &vp->write_buf;
   1826 	} else {
   1827 	    bp = 0;
   1828 	}
   1829 	switch (command & ~VSTREAM_BST_MASK_DIR) {
   1830 	case VSTREAM_BST_FLAG_PEND:
   1831 	    return (bp ? bp->len - bp->cnt : 0);
   1832 	    /* Add other requests below. */
   1833 	}
   1834 	break;
   1835     }
   1836     msg_panic("vstream_bufstat: unknown command: %d", command);
   1837 }
   1838 
   1839 #undef vstream_peek			/* API binary compatibility. */
   1840 
   1841 /* vstream_peek - peek at a stream */
   1842 
   1843 ssize_t vstream_peek(VSTREAM *vp)
   1844 {
   1845     if (vp->buf.flags & VSTREAM_FLAG_READ) {
   1846 	return (-vp->buf.cnt);
   1847     } else if (vp->buf.flags & VSTREAM_FLAG_DOUBLE) {
   1848 	return (-vp->read_buf.cnt);
   1849     } else {
   1850 	return (0);
   1851     }
   1852 }
   1853 
   1854 /* vstream_peek_data - peek at unread data */
   1855 
   1856 const char *vstream_peek_data(VSTREAM *vp)
   1857 {
   1858     if (vp->buf.flags & VSTREAM_FLAG_READ) {
   1859 	return ((const char *) vp->buf.ptr);
   1860     } else if (vp->buf.flags & VSTREAM_FLAG_DOUBLE) {
   1861 	return ((const char *) vp->read_buf.ptr);
   1862     } else {
   1863 	return (0);
   1864     }
   1865 }
   1866 
   1867 /* vstream_memopen - open a VSTRING */
   1868 
   1869 VSTREAM *vstream_memreopen(VSTREAM *stream, VSTRING *string, int flags)
   1870 {
   1871     if (stream == 0)
   1872 	stream = vstream_subopen();
   1873     else if ((stream->buf.flags & VSTREAM_FLAG_MEMORY) == 0)
   1874 	msg_panic("vstream_memreopen: cannot reopen non-memory stream");
   1875     stream->fd = -1;
   1876     stream->read_fn = 0;
   1877     stream->write_fn = 0;
   1878     stream->vstring = string;
   1879     memcpy(&stream->buf, &stream->vstring->vbuf, sizeof(stream->buf));
   1880     stream->buf.flags |= VSTREAM_FLAG_MEMORY;
   1881     switch (VSTREAM_ACC_MASK(flags)) {
   1882     case O_RDONLY:
   1883 	stream->buf.flags |= VSTREAM_FLAG_READ;
   1884 	/* Prevent reading unwritten data after vstream_fseek(). */
   1885 	stream->buf.len = stream->buf.ptr - stream->buf.data;
   1886 	VSTREAM_BUF_AT_OFFSET(&stream->buf, 0);
   1887 	break;
   1888     case O_WRONLY:
   1889 	stream->buf.flags |= VSTREAM_FLAG_WRITE;
   1890 	VSTREAM_BUF_AT_OFFSET(&stream->buf, 0);
   1891 	break;
   1892     case O_APPEND:
   1893 	stream->buf.flags |= VSTREAM_FLAG_WRITE;
   1894 	VSTREAM_BUF_AT_OFFSET(&stream->buf,
   1895 			      stream->buf.ptr - stream->buf.data);
   1896 	break;
   1897     default:
   1898 	msg_panic("vstream_memopen: flags must be one of "
   1899 		  "O_RDONLY, O_WRONLY, or O_APPEND");
   1900     }
   1901     return (stream);
   1902 }
   1903 
   1904 /* vstream_no_debug - debug logging lockout */
   1905 
   1906 void    vstream_no_debug(VSTREAM *stream)
   1907 {
   1908     vstream_log_veto = stream;
   1909 }
   1910 
   1911 #ifdef TEST
   1912 
   1913 static void copy_line(ssize_t bufsize)
   1914 {
   1915     int     c;
   1916 
   1917     /*
   1918      * Demonstrates that VSTREAM_CTL_BUFSIZE increases the buffer size, but
   1919      * does not decrease it. Uses VSTREAM_ERR for non-test output to avoid
   1920      * interfering with the test.
   1921      */
   1922     vstream_fprintf(VSTREAM_ERR, "buffer size test: copy text with %ld buffer size, ignore requests to shrink\n",
   1923 		    (long) bufsize);
   1924     vstream_fflush(VSTREAM_ERR);
   1925     vstream_control(VSTREAM_IN, CA_VSTREAM_CTL_BUFSIZE(bufsize), VSTREAM_CTL_END);
   1926     vstream_control(VSTREAM_OUT, CA_VSTREAM_CTL_BUFSIZE(bufsize), VSTREAM_CTL_END);
   1927     while ((c = VSTREAM_GETC(VSTREAM_IN)) != VSTREAM_EOF) {
   1928 	VSTREAM_PUTC(c, VSTREAM_OUT);
   1929 	if (c == '\n')
   1930 	    break;
   1931     }
   1932     vstream_fflush(VSTREAM_OUT);
   1933     vstream_fprintf(VSTREAM_ERR, "actual read/write buffer sizes: %ld/%ld\n\n",
   1934 		    (long) VSTREAM_IN->buf.len, (long) VSTREAM_OUT->buf.len);
   1935     vstream_fflush(VSTREAM_ERR);
   1936 }
   1937 
   1938 static void printf_number(void)
   1939 {
   1940 
   1941     /*
   1942      * Demonstrates that vstream_printf() use vbuf_print().
   1943      */
   1944     vstream_printf("formatting test: print a number\n");
   1945     vstream_printf("%d\n\n", 1234567890);
   1946     vstream_fflush(VSTREAM_OUT);
   1947 }
   1948 
   1949 static void do_memory_stream(void)
   1950 {
   1951     VSTRING *buf = vstring_alloc(1);
   1952     VSTREAM *fp;
   1953     off_t   offset;
   1954     int     ch;
   1955 
   1956     /*
   1957      * Preload the string.
   1958      */
   1959     vstream_printf("memory stream test prep: prefill the VSTRING\n");
   1960     vstring_strcpy(buf, "01234567");
   1961     vstream_printf("VSTRING content length: %ld/%ld, content: %s\n",
   1962 		   (long) VSTRING_LEN(buf), (long) buf->vbuf.len,
   1963 		   vstring_str(buf));
   1964     VSTREAM_PUTCHAR('\n');
   1965     vstream_fflush(VSTREAM_OUT);
   1966 
   1967     /*
   1968      * Test: open the memory VSTREAM in write-only mode, and clobber it.
   1969      */
   1970     vstream_printf("memory stream test: open the VSTRING for writing, overwrite, close\n");
   1971     fp = vstream_memopen(buf, O_WRONLY);
   1972     vstream_printf("initial memory VSTREAM write offset: %ld/%ld\n",
   1973 		   (long) vstream_ftell(fp), (long) fp->buf.len);
   1974     vstream_fprintf(fp, "hallo");
   1975     vstream_printf("final memory VSTREAM write offset: %ld/%ld\n",
   1976 		   (long) vstream_ftell(fp), (long) fp->buf.len);
   1977     vstream_fclose(fp);
   1978     vstream_printf("VSTRING content length: %ld/%ld, content: %s\n",
   1979 		   (long) VSTRING_LEN(buf), (long) buf->vbuf.len,
   1980 		   vstring_str(buf));
   1981     VSTREAM_PUTCHAR('\n');
   1982     vstream_fflush(VSTREAM_OUT);
   1983 
   1984     /*
   1985      * Test: open the memory VSTREAM for append. vstream_memopen() sets the
   1986      * buffer length to the VSTRING buffer length, and positions the write
   1987      * pointer at the VSTRING write position. Write some content, then
   1988      * overwrite one character.
   1989      */
   1990     vstream_printf("memory stream test: open the VSTRING for append, write multiple, then overwrite 1\n");
   1991     fp = vstream_memopen(buf, O_APPEND);
   1992     vstream_printf("initial memory VSTREAM write offset: %ld/%ld\n",
   1993 		   (long) vstream_ftell(fp), (long) fp->buf.len);
   1994     vstream_fprintf(fp, " world");
   1995     vstream_printf("final memory VSTREAM write offset: %ld/%ld\n",
   1996 		   (long) vstream_ftell(fp), (long) fp->buf.len);
   1997     if (vstream_fflush(fp))
   1998 	msg_fatal("vstream_fflush: %m");
   1999     vstream_printf("VSTRING content length: %ld/%ld, content: %s\n",
   2000 		   (long) VSTRING_LEN(buf), (long) buf->vbuf.len,
   2001 		   vstring_str(buf));
   2002     VSTREAM_PUTCHAR('\n');
   2003 
   2004     /*
   2005      * While the stream is still open, replace the second character.
   2006      */
   2007     vstream_printf("replace second character and close\n");
   2008     if ((offset = vstream_fseek(fp, 1, SEEK_SET)) != 1)
   2009 	msg_panic("unexpected vstream_fseek return: %ld, expected: %ld",
   2010 		  (long) offset, (long) 1);
   2011     VSTREAM_PUTC('e', fp);
   2012 
   2013     /*
   2014      * Skip to the end of the content, so that vstream_fflush() will update
   2015      * the VSTRING with the right content length.
   2016      */
   2017     if ((offset = vstream_fseek(fp, VSTRING_LEN(buf), SEEK_SET)) != VSTRING_LEN(buf))
   2018 	msg_panic("unexpected vstream_fseek return: %ld, expected: %ld",
   2019 		  (long) offset, (long) VSTRING_LEN(buf));
   2020     vstream_fclose(fp);
   2021 
   2022     vstream_printf("VSTRING content length: %ld/%ld, content: %s\n",
   2023 		   (long) VSTRING_LEN(buf), (long) buf->vbuf.len,
   2024 		   vstring_str(buf));
   2025     VSTREAM_PUTCHAR('\n');
   2026     vstream_fflush(VSTREAM_OUT);
   2027 
   2028     /*
   2029      * TODO: test that in write/append mode, seek past the end of data will
   2030      * result in zero-filled space.
   2031      */
   2032 
   2033     /*
   2034      * Test: Open the VSTRING for reading. This time, vstream_memopen() will
   2035      * set the VSTREAM buffer length to the content length of the VSTRING, so
   2036      * that it won't attempt to read past the end of the content.
   2037      */
   2038     vstream_printf("memory stream test: open VSTRING for reading, then read\n");
   2039     fp = vstream_memopen(buf, O_RDONLY);
   2040     vstream_printf("initial memory VSTREAM read offset: %ld/%ld\n",
   2041 		   (long) vstream_ftell(fp), (long) fp->buf.len);
   2042     vstream_printf("reading memory VSTREAM: ");
   2043     while ((ch = VSTREAM_GETC(fp)) != VSTREAM_EOF)
   2044 	VSTREAM_PUTCHAR(ch);
   2045     VSTREAM_PUTCHAR('\n');
   2046     vstream_printf("final memory VSTREAM read offset: %ld/%ld\n",
   2047 		   (long) vstream_ftell(fp), (long) fp->buf.len);
   2048     vstream_printf("seeking to offset %ld should work: ",
   2049 		   (long) fp->buf.len + 1);
   2050     vstream_fflush(VSTREAM_OUT);
   2051     if ((offset = vstream_fseek(fp, fp->buf.len + 1, SEEK_SET)) != fp->buf.len + 1)
   2052 	msg_panic("unexpected vstream_fseek return: %ld, expected: %ld",
   2053 		  (long) offset, (long) fp->buf.len + 1);
   2054     vstream_printf("PASS\n");
   2055     vstream_fflush(VSTREAM_OUT);
   2056     vstream_printf("VSTREAM_GETC should return VSTREAM_EOF\n");
   2057     ch = VSTREAM_GETC(fp);
   2058     if (ch != VSTREAM_EOF)
   2059 	msg_panic("unexpected vstream_fseek VSTREAM_GETC return: %d, expected: %d",
   2060 		  ch, VSTREAM_EOF);
   2061     vstream_printf("PASS\n");
   2062     vstream_printf("final memory VSTREAM read offset: %ld/%ld\n",
   2063 		   (long) vstream_ftell(fp), (long) fp->buf.len);
   2064     vstream_printf("VSTRING content length: %ld/%ld, content: %s\n",
   2065 		   (long) VSTRING_LEN(buf), (long) buf->vbuf.len,
   2066 		   vstring_str(buf));
   2067     VSTREAM_PUTCHAR('\n');
   2068     vstream_fflush(VSTREAM_OUT);
   2069     vstream_fclose(fp);
   2070     vstring_free(buf);
   2071 }
   2072 
   2073  /*
   2074   * Exercise some of the features.
   2075   */
   2076 
   2077 #include <msg_vstream.h>
   2078 
   2079 int     main(int argc, char **argv)
   2080 {
   2081     msg_vstream_init(argv[0], VSTREAM_ERR);
   2082 
   2083     /*
   2084      * Test buffer expansion and shrinking. Formatted print may silently
   2085      * expand the write buffer and cause multiple bytes to be written.
   2086      */
   2087     copy_line(1);				/* one-byte read/write */
   2088     copy_line(2);				/* two-byte read/write */
   2089     copy_line(1);				/* two-byte read/write */
   2090     printf_number();				/* multi-byte write */
   2091     do_memory_stream();
   2092 
   2093     exit(0);
   2094 }
   2095 
   2096 #endif
   2097