1 /*- 2 * Copyright (c) 1990 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Chris Torek. 7 * 8 * %sccs.include.redist.c% 9 */ 10 11 #if defined(LIBC_SCCS) && !defined(lint) 12 static char sccsid[] = "@(#)fvwrite.c 5.1 (Berkeley) 01/20/91"; 13 #endif /* LIBC_SCCS and not lint */ 14 15 #include <stdio.h> 16 #include <string.h> 17 #include "local.h" 18 #include "fvwrite.h" 19 20 /* 21 * Write some memory regions. Return zero on success, EOF on error. 22 * 23 * This routine is large and unsightly, but most of the ugliness due 24 * to the three different kinds of output buffering is handled here. 25 */ 26 __sfvwrite(fp, uio) 27 register FILE *fp; 28 register struct __suio *uio; 29 { 30 register size_t len; 31 register char *p; 32 register struct __siov *iov; 33 register int w, s; 34 char *nl; 35 int nlknown, nldist; 36 37 if ((len = uio->uio_resid) == 0) 38 return (0); 39 /* make sure we can write */ 40 if (cantwrite(fp)) 41 return (EOF); 42 43 #define MIN(a, b) ((a) < (b) ? (a) : (b)) 44 #define COPY(n) (void) memcpy((void *)fp->_p, (void *)p, (size_t)(n)); 45 46 iov = uio->uio_iov; 47 len = 0; 48 #define GETIOV(extra_work) \ 49 while (len == 0) { \ 50 extra_work; \ 51 p = iov->iov_base; \ 52 len = iov->iov_len; \ 53 iov++; \ 54 } 55 if (fp->_flags & __SNBF) { 56 /* 57 * Unbuffered: write up to BUFSIZ bytes at a time. 58 */ 59 do { 60 GETIOV(;); 61 w = (*fp->_write)(fp->_cookie, p, MIN(len, BUFSIZ)); 62 if (w <= 0) 63 goto err; 64 p += w; 65 len -= w; 66 } while ((uio->uio_resid -= w) != 0); 67 } else if ((fp->_flags & __SLBF) == 0) { 68 /* 69 * Fully buffered: fill partially full buffer, if any, 70 * and then flush. If there is no partial buffer, write 71 * one _bf._size byte chunk directly (without copying). 72 * 73 * String output is a special case: write as many bytes 74 * as fit, but pretend we wrote everything. This makes 75 * snprintf() return the number of bytes needed, rather 76 * than the number used, and avoids its write function 77 * (so that the write function can be invalid). 78 */ 79 do { 80 GETIOV(;); 81 w = fp->_w; 82 if (fp->_flags & __SSTR) { 83 if (len < w) 84 w = len; 85 COPY(w); /* copy MIN(fp->_w,len), */ 86 fp->_w -= w; 87 fp->_p += w; 88 w = len; /* but pretend copied all */ 89 } else if (fp->_p > fp->_bf._base && len > w) { 90 /* fill and flush */ 91 COPY(w); 92 /* fp->_w -= w; */ /* unneeded */ 93 fp->_p += w; 94 if (fflush(fp)) 95 goto err; 96 } else if (len >= (w = fp->_bf._size)) { 97 /* write directly */ 98 w = (*fp->_write)(fp->_cookie, p, w); 99 if (w <= 0) 100 goto err; 101 } else { 102 /* fill and done */ 103 w = len; 104 COPY(w); 105 fp->_w -= w; 106 fp->_p += w; 107 } 108 p += w; 109 len -= w; 110 } while ((uio->uio_resid -= w) != 0); 111 } else { 112 /* 113 * Line buffered: like fully buffered, but we 114 * must check for newlines. Compute the distance 115 * to the first newline (including the newline), 116 * or `infinity' if there is none, then pretend 117 * that the amount to write is MIN(len,nldist). 118 */ 119 nlknown = 0; 120 do { 121 GETIOV(nlknown = 0); 122 if (!nlknown) { 123 nl = memchr((void *)p, '\n', len); 124 nldist = nl ? nl + 1 - p : len + 1; 125 nlknown = 1; 126 } 127 s = MIN(len, nldist); 128 w = fp->_w + fp->_bf._size; 129 if (fp->_p > fp->_bf._base && s > w) { 130 COPY(w); 131 /* fp->_w -= w; */ 132 fp->_p += w; 133 if (fflush(fp)) 134 goto err; 135 } else if (s >= (w = fp->_bf._size)) { 136 w = (*fp->_write)(fp->_cookie, p, w); 137 if (w <= 0) 138 goto err; 139 } else { 140 w = s; 141 COPY(w); 142 fp->_w -= w; 143 fp->_p += w; 144 } 145 if ((nldist -= w) == 0) { 146 /* copied the newline: flush and forget */ 147 if (fflush(fp)) 148 goto err; 149 nlknown = 0; 150 } 151 p += w; 152 len -= w; 153 } while ((uio->uio_resid -= w) != 0); 154 } 155 return (0); 156 157 err: 158 fp->_flags |= __SERR; 159 return (EOF); 160 } 161