No empty .Rs/.Re
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / util / vstream.c
blobfc85bea10032b21226356e54ad5b4d754d25e282
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* vstream 3
6 /* SUMMARY
7 /* light-weight buffered I/O package
8 /* SYNOPSIS
9 /* #include <vstream.h>
11 /* VSTREAM *vstream_fopen(path, flags, mode)
12 /* const char *path;
13 /* int flags;
14 /* mode_t mode;
16 /* VSTREAM *vstream_fdopen(fd, flags)
17 /* int fd;
18 /* int flags;
20 /* int vstream_fclose(stream)
21 /* VSTREAM *stream;
23 /* int vstream_fdclose(stream)
24 /* VSTREAM *stream;
26 /* VSTREAM *vstream_printf(format, ...)
27 /* const char *format;
29 /* VSTREAM *vstream_fprintf(stream, format, ...)
30 /* VSTREAM *stream;
31 /* const char *format;
33 /* int VSTREAM_GETC(stream)
34 /* VSTREAM *stream;
36 /* int VSTREAM_PUTC(ch, stream)
37 /* int ch;
39 /* int VSTREAM_GETCHAR(void)
41 /* int VSTREAM_PUTCHAR(ch)
42 /* int ch;
44 /* int vstream_ungetc(stream, ch)
45 /* VSTREAM *stream;
46 /* int ch;
48 /* int vstream_fputs(str, stream)
49 /* const char *str;
50 /* VSTREAM *stream;
52 /* off_t vstream_ftell(stream)
53 /* VSTREAM *stream;
55 /* off_t vstream_fseek(stream, offset, whence)
56 /* VSTREAM *stream;
57 /* off_t offset;
58 /* int whence;
60 /* int vstream_fflush(stream)
61 /* VSTREAM *stream;
63 /* int vstream_fpurge(stream, direction)
64 /* VSTREAM *stream;
65 /* int direction;
67 /* ssize_t vstream_fread(stream, buf, len)
68 /* VSTREAM *stream;
69 /* char *buf;
70 /* ssize_t len;
72 /* ssize_t vstream_fwrite(stream, buf, len)
73 /* VSTREAM *stream;
74 /* const char *buf;
75 /* ssize_t len;
77 /* void vstream_control(stream, name, ...)
78 /* VSTREAM *stream;
79 /* int name;
81 /* int vstream_fileno(stream)
82 /* VSTREAM *stream;
84 /* void *vstream_context(stream)
85 /* VSTREAM *stream;
87 /* int vstream_ferror(stream)
88 /* VSTREAM *stream;
90 /* int vstream_ftimeout(stream)
91 /* VSTREAM *stream;
93 /* int vstream_feof(stream)
94 /* VSTREAM *stream;
96 /* int vstream_clearerr(stream)
97 /* VSTREAM *stream;
99 /* const char *VSTREAM_PATH(stream)
100 /* VSTREAM *stream;
102 /* char *vstream_vfprintf(vp, format, ap)
103 /* const char *format;
104 /* va_list *ap;
106 /* ssize_t vstream_bufstat(stream, command)
107 /* VSTREAM *stream;
108 /* int command;
110 /* ssize_t vstream_peek(stream)
111 /* VSTREAM *stream;
113 /* int vstream_setjmp(stream)
114 /* VSTREAM *stream;
116 /* void vstream_longjmp(stream, val)
117 /* VSTREAM *stream;
118 /* int val;
120 /* time_t vstream_ftime(stream)
121 /* VSTREAM *stream;
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.
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.
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().
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.
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().
172 /* vstream_fdclose() leaves the file(s) open but is otherwise
173 /* identical to vstream_fclose().
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.
184 /* vstream_printf() performs formatted output to the standard output
185 /* stream.
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.
192 /* VSTREAM_GETCHAR() is an alias for VSTREAM_GETC(VSTREAM_IN).
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.
199 /* VSTREAM_PUTCHAR(c) is an alias for VSTREAM_PUTC(c, VSTREAM_OUT).
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.
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.
211 /* vstream_ftell() returns the file offset for the specified stream,
212 /* -1 if the stream is connected to a non-seekable file.
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.
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().
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.
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.
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.
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.
301 /* vstream_context() returns the application context that is passed on to
302 /* the application-specified read/write routines.
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.
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.
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.
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.
323 /* vstream_clearerr() resets the timeout, error and end-of-file indication
324 /* of the specified stream, and returns no useful result.
326 /* vstream_vfprintf() provides an alternate interface
327 /* for formatting an argument list according to a format string.
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).
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().
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.
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.
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 /*--*/
379 /* System library. */
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>
391 /* Utility library. */
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"
400 /* Application-specific. */
403 * Forward declarations.
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);
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.
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.
421 static unsigned char vstream_fstd_buf[VSTREAM_BUFSIZE];
423 VSTREAM vstream_fstd[] = {
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,},
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,},
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,},
441 #define VSTREAM_STATIC(v) ((v) >= VSTREAM_IN && (v) <= VSTREAM_ERR)
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.
447 #define VSTREAM_ACC_MASK(f) ((f) & (O_APPEND | O_WRONLY | O_RDWR))
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)
455 #define VSTREAM_BUF_COUNT(bp, n) \
456 ((bp)->flags & VSTREAM_FLAG_READ ? -(n) : (n))
458 #define VSTREAM_BUF_AT_START(bp) { \
459 (bp)->cnt = VSTREAM_BUF_COUNT((bp), (bp)->len); \
460 (bp)->ptr = (bp)->data; \
463 #define VSTREAM_BUF_AT_OFFSET(bp, offset) { \
464 (bp)->ptr = (bp)->data + (offset); \
465 (bp)->cnt = VSTREAM_BUF_COUNT(bp, (bp)->len - (offset)); \
468 #define VSTREAM_BUF_AT_END(bp) { \
469 (bp)->cnt = 0; \
470 (bp)->ptr = (bp)->data + (bp)->len; \
473 #define VSTREAM_BUF_ZERO(bp) { \
474 (bp)->flags = 0; \
475 (bp)->data = (bp)->ptr = 0; \
476 (bp)->len = (bp)->cnt = 0; \
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); \
485 #define VSTREAM_SAVE_STATE(stream, buffer, filedes) { \
486 stream->buffer = stream->buf; \
487 stream->filedes = stream->fd; \
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)
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; \
504 #define VSTREAM_FLAG_READ_DOUBLE (VSTREAM_FLAG_READ | VSTREAM_FLAG_DOUBLE)
505 #define VSTREAM_FLAG_WRITE_DOUBLE (VSTREAM_FLAG_WRITE | VSTREAM_FLAG_DOUBLE)
507 #define VSTREAM_FFLUSH_SOME(stream) \
508 vstream_fflush_some((stream), (stream)->buf.len - (stream)->buf.cnt)
510 /* vstream_buf_init - initialize buffer */
512 static void vstream_buf_init(VBUF *bp, int flags)
516 * Initialize the buffer such that the first data access triggers a
517 * buffer boundary action.
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);
526 /* vstream_buf_alloc - allocate buffer memory */
528 static void vstream_buf_alloc(VBUF *bp, ssize_t len)
530 VSTREAM *stream = VBUF_TO_APPL(bp, VSTREAM, buf);
531 ssize_t used = bp->ptr - bp->data;
532 const char *myname = "vstream_buf_alloc";
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);
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.
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);
557 /* vstream_buf_wipe - reset buffer to initial state */
559 static void vstream_buf_wipe(VBUF *bp)
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);
567 /* vstream_fflush_some - flush some buffered data */
569 static int vstream_fflush_some(VSTREAM *stream, ssize_t to_flush)
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;
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.
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);
598 used = bp->len - bp->cnt;
599 left_over = used - to_flush;
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);
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.
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);
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);
630 if (bp->flags & VSTREAM_FLAG_SEEK)
631 stream->offset += to_flush;
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.
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);
646 /* vstream_fflush_delayed - delayed stream flush for double-buffered stream */
648 static int vstream_fflush_delayed(VSTREAM *stream)
650 int status;
653 * Sanity check.
655 if ((stream->buf.flags & VSTREAM_FLAG_READ_DOUBLE) != VSTREAM_FLAG_READ_DOUBLE)
656 msg_panic("vstream_fflush_delayed: bad flags");
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.
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);
669 status = VSTREAM_FFLUSH_SOME(stream);
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);
676 return (status);
679 /* vstream_buf_get_ready - vbuf callback to make buffer ready for reading */
681 static int vstream_buf_get_ready(VBUF *bp)
683 VSTREAM *stream = VBUF_TO_APPL(bp, VSTREAM, buf);
684 const char *myname = "vstream_buf_get_ready";
685 ssize_t n;
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.
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);
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);
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.
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);
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.
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);
739 * Did we receive an EOF indication?
741 if (bp->flags & VSTREAM_FLAG_EOF)
742 return (VSTREAM_EOF);
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.
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);
771 /* vstream_buf_put_ready - vbuf callback to make buffer ready for writing */
773 static int vstream_buf_put_ready(VBUF *bp)
775 VSTREAM *stream = VBUF_TO_APPL(bp, VSTREAM, buf);
776 const char *myname = "vstream_buf_put_ready";
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.
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);
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.
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);
815 return (0);
818 /* vstream_buf_space - reserve space ahead of time */
820 static int vstream_buf_space(VBUF *bp, ssize_t want)
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";
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.
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);
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.
860 #define VSTREAM_TRUNCATE(count, base) (((count) / (base)) * (base))
861 #define VSTREAM_ROUNDUP(count, base) VSTREAM_TRUNCATE(count + base - 1, base)
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);
879 return (vstream_ferror(stream) ? VSTREAM_EOF : 0); /* mmap() may fail */
882 /* vstream_fpurge - discard unread or unwritten content */
884 int vstream_fpurge(VSTREAM *stream, int direction)
886 const char *myname = "vstream_fpurge";
887 VBUF *bp = &stream->buf;
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))
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.
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.
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);
926 * Invalidate the cached file seek position.
928 bp->flags &= ~VSTREAM_FLAG_SEEK;
929 stream->offset = 0;
931 return (0);
934 /* vstream_fseek - change I/O position */
936 off_t vstream_fseek(VSTREAM *stream, off_t offset, int whence)
938 const char *myname = "vstream_fseek";
939 VBUF *bp = &stream->buf;
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.
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);
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);
972 * Clear the read/write flags to inform the buffer boundary action
973 * routines that we may have changed I/O position.
975 bp->flags &= ~(VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE);
978 * Shave an unnecessary system call.
980 if (bp->flags & VSTREAM_FLAG_NSEEK) {
981 errno = ESPIPE;
982 return (-1);
986 * Update the cached file seek position.
988 if ((stream->offset = lseek(stream->fd, offset, whence)) < 0) {
989 bp->flags |= VSTREAM_FLAG_NSEEK;
990 } else {
991 bp->flags |= VSTREAM_FLAG_SEEK;
993 bp->flags &= ~VSTREAM_FLAG_EOF;
994 return (stream->offset);
997 /* vstream_ftell - return file offset */
999 off_t vstream_ftell(VSTREAM *stream)
1001 VBUF *bp = &stream->buf;
1004 * Shave an unnecessary syscall.
1006 if (bp->flags & VSTREAM_FLAG_NSEEK) {
1007 errno = ESPIPE;
1008 return (-1);
1012 * Use the cached file offset when available. This is the offset after
1013 * the last read, write or seek operation.
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);
1020 bp->flags |= VSTREAM_FLAG_SEEK;
1024 * If this is a read buffer, subtract the number of unread bytes from the
1025 * cached offset. Remember that read counts are negative.
1027 if (bp->flags & VSTREAM_FLAG_READ)
1028 return (stream->offset + bp->cnt);
1031 * If this is a write buffer, add the number of unwritten bytes to the
1032 * cached offset.
1034 if (bp->flags & VSTREAM_FLAG_WRITE)
1035 return (stream->offset + (bp->ptr - bp->data));
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.
1041 return (stream->offset);
1044 /* vstream_fdopen - add buffering to pre-opened stream */
1046 VSTREAM *vstream_fdopen(int fd, int flags)
1048 VSTREAM *stream;
1051 * Sanity check.
1053 if (fd < 0)
1054 msg_panic("vstream_fdopen: bad file %d", fd);
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?).
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);
1080 /* vstream_fopen - open buffered file stream */
1082 VSTREAM *vstream_fopen(const char *path, int flags, mode_t mode)
1084 VSTREAM *stream;
1085 int fd;
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);
1096 /* vstream_fflush - flush write buffer */
1098 int vstream_fflush(VSTREAM *stream)
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));
1107 /* vstream_fclose - close buffered stream */
1109 int vstream_fclose(VSTREAM *stream)
1111 int err;
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.
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);
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);
1148 /* vstream_fdclose - close stream, leave file(s) open */
1150 int vstream_fdclose(VSTREAM *stream)
1154 * Flush unwritten output, just like vstream_fclose(). Errors are
1155 * reported by vstream_fclose().
1157 if ((stream->buf.flags & VSTREAM_FLAG_WRITE_DOUBLE) != 0)
1158 (void) vstream_fflush(stream);
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.
1166 if (stream->buf.flags & VSTREAM_FLAG_DOUBLE) {
1167 stream->fd = stream->read_fd = stream->write_fd = -1;
1168 } else {
1169 stream->fd = -1;
1171 return (vstream_fclose(stream));
1174 /* vstream_printf - formatted print to stdout */
1176 VSTREAM *vstream_printf(const char *fmt,...)
1178 VSTREAM *stream = VSTREAM_OUT;
1179 va_list ap;
1181 va_start(ap, fmt);
1182 vbuf_print(&stream->buf, fmt, ap);
1183 va_end(ap);
1184 return (stream);
1187 /* vstream_fprintf - formatted print to buffered stream */
1189 VSTREAM *vstream_fprintf(VSTREAM *stream, const char *fmt,...)
1191 va_list ap;
1193 va_start(ap, fmt);
1194 vbuf_print(&stream->buf, fmt, ap);
1195 va_end(ap);
1196 return (stream);
1199 /* vstream_fputs - write string to stream */
1201 int vstream_fputs(const char *str, VSTREAM *stream)
1203 int ch;
1205 while ((ch = *str++) != 0)
1206 if (VSTREAM_PUTC(ch, stream) == VSTREAM_EOF)
1207 return (VSTREAM_EOF);
1208 return (0);
1211 /* vstream_control - fine control */
1213 void vstream_control(VSTREAM *stream, int name,...)
1215 const char *myname = "vstream_control";
1216 va_list ap;
1217 int floor;
1218 int old_fd;
1219 ssize_t req_bufsize = 0;
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);
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;
1275 #ifdef VSTREAM_CTL_DUPFD
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); \
1283 } while (0)
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);
1298 break;
1299 #endif
1302 * Postpone memory (re)allocation until the space is needed.
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);
1319 va_end(ap);
1322 /* vstream_vfprintf - formatted print engine */
1324 VSTREAM *vstream_vfprintf(VSTREAM *vp, const char *format, va_list ap)
1326 vbuf_print(&vp->buf, format, ap);
1327 return (vp);
1330 /* vstream_bufstat - get stream buffer status */
1332 ssize_t vstream_bufstat(VSTREAM *vp, int command)
1334 VBUF *bp;
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;
1345 switch (command & ~VSTREAM_BST_MASK_DIR) {
1346 case VSTREAM_BST_FLAG_PEND:
1347 return (bp ? -bp->cnt : 0);
1348 /* Add other requests below. */
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;
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. */
1364 break;
1366 msg_panic("vstream_bufstat: unknown command: %d", command);
1369 #undef vstream_peek /* API binary compatibility. */
1371 /* vstream_peek - peek at a stream */
1373 ssize_t vstream_peek(VSTREAM *vp)
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);