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.2 (Berkeley) 02/05/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 p = iov->iov_base; 48 len = iov->iov_len; 49 iov++; 50 #define GETIOV(extra_work) \ 51 while (len == 0) { \ 52 extra_work; \ 53 p = iov->iov_base; \ 54 len = iov->iov_len; \ 55 iov++; \ 56 } 57 if (fp->_flags & __SNBF) { 58 /* 59 * Unbuffered: write up to BUFSIZ bytes at a time. 60 */ 61 do { 62 GETIOV(;); 63 w = (*fp->_write)(fp->_cookie, p, MIN(len, BUFSIZ)); 64 if (w <= 0) 65 goto err; 66 p += w; 67 len -= w; 68 } while ((uio->uio_resid -= w) != 0); 69 } else if ((fp->_flags & __SLBF) == 0) { 70 /* 71 * Fully buffered: fill partially full buffer, if any, 72 * and then flush. If there is no partial buffer, write 73 * one _bf._size byte chunk directly (without copying). 74 * 75 * String output is a special case: write as many bytes 76 * as fit, but pretend we wrote everything. This makes 77 * snprintf() return the number of bytes needed, rather 78 * than the number used, and avoids its write function 79 * (so that the write function can be invalid). 80 */ 81 do { 82 GETIOV(;); 83 w = fp->_w; 84 if (fp->_flags & __SSTR) { 85 if (len < w) 86 w = len; 87 COPY(w); /* copy MIN(fp->_w,len), */ 88 fp->_w -= w; 89 fp->_p += w; 90 w = len; /* but pretend copied all */ 91 } else if (fp->_p > fp->_bf._base && len > w) { 92 /* fill and flush */ 93 COPY(w); 94 /* fp->_w -= w; */ /* unneeded */ 95 fp->_p += w; 96 if (fflush(fp)) 97 goto err; 98 } else if (len >= (w = fp->_bf._size)) { 99 /* write directly */ 100 w = (*fp->_write)(fp->_cookie, p, w); 101 if (w <= 0) 102 goto err; 103 } else { 104 /* fill and done */ 105 w = len; 106 COPY(w); 107 fp->_w -= w; 108 fp->_p += w; 109 } 110 p += w; 111 len -= w; 112 } while ((uio->uio_resid -= w) != 0); 113 } else { 114 /* 115 * Line buffered: like fully buffered, but we 116 * must check for newlines. Compute the distance 117 * to the first newline (including the newline), 118 * or `infinity' if there is none, then pretend 119 * that the amount to write is MIN(len,nldist). 120 */ 121 nlknown = 0; 122 nldist = 0; /* XXX just to keep gcc happy */ 123 do { 124 GETIOV(nlknown = 0); 125 if (!nlknown) { 126 nl = memchr((void *)p, '\n', len); 127 nldist = nl ? nl + 1 - p : len + 1; 128 nlknown = 1; 129 } 130 s = MIN(len, nldist); 131 w = fp->_w + fp->_bf._size; 132 if (fp->_p > fp->_bf._base && s > w) { 133 COPY(w); 134 /* fp->_w -= w; */ 135 fp->_p += w; 136 if (fflush(fp)) 137 goto err; 138 } else if (s >= (w = fp->_bf._size)) { 139 w = (*fp->_write)(fp->_cookie, p, w); 140 if (w <= 0) 141 goto err; 142 } else { 143 w = s; 144 COPY(w); 145 fp->_w -= w; 146 fp->_p += w; 147 } 148 if ((nldist -= w) == 0) { 149 /* copied the newline: flush and forget */ 150 if (fflush(fp)) 151 goto err; 152 nlknown = 0; 153 } 154 p += w; 155 len -= w; 156 } while ((uio->uio_resid -= w) != 0); 157 } 158 return (0); 159 160 err: 161 fp->_flags |= __SERR; 162 return (EOF); 163 } 164