1 /* $NetBSD: fvwrite.c,v 1.15 2003/08/07 16:43:26 agc Exp $ */ 2 3 /*- 4 * Copyright (c) 1990, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Chris Torek. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/cdefs.h> 36 #if defined(LIBC_SCCS) && !defined(lint) 37 #if 0 38 static char sccsid[] = "@(#)fvwrite.c 8.1 (Berkeley) 6/4/93"; 39 #else 40 __RCSID("$NetBSD: fvwrite.c,v 1.15 2003/08/07 16:43:26 agc Exp $"); 41 #endif 42 #endif /* LIBC_SCCS and not lint */ 43 44 #include <assert.h> 45 #include <errno.h> 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include "reentrant.h" 50 #include "local.h" 51 #include "fvwrite.h" 52 53 /* 54 * Write some memory regions. Return zero on success, EOF on error. 55 * 56 * This routine is large and unsightly, but most of the ugliness due 57 * to the three different kinds of output buffering is handled here. 58 */ 59 int 60 __sfvwrite(fp, uio) 61 FILE *fp; 62 struct __suio *uio; 63 { 64 size_t len; 65 char *p; 66 struct __siov *iov; 67 int w, s; 68 char *nl; 69 int nlknown, nldist; 70 71 _DIAGASSERT(fp != NULL); 72 _DIAGASSERT(uio != NULL); 73 74 if ((len = uio->uio_resid) == 0) 75 return (0); 76 /* make sure we can write */ 77 if (cantwrite(fp)) { 78 errno = EBADF; 79 return (EOF); 80 } 81 82 #define MIN(a, b) ((a) < (b) ? (a) : (b)) 83 #define COPY(n) (void)memcpy((void *)fp->_p, (void *)p, (size_t)(n)) 84 85 iov = uio->uio_iov; 86 p = iov->iov_base; 87 len = iov->iov_len; 88 iov++; 89 #define GETIOV(extra_work) \ 90 while (len == 0) { \ 91 extra_work; \ 92 p = iov->iov_base; \ 93 len = iov->iov_len; \ 94 iov++; \ 95 } 96 if (fp->_flags & __SNBF) { 97 /* 98 * Unbuffered: write up to BUFSIZ bytes at a time. 99 */ 100 do { 101 GETIOV(;); 102 w = (*fp->_write)(fp->_cookie, p, 103 (int)MIN(len, BUFSIZ)); 104 if (w <= 0) 105 goto err; 106 p += w; 107 len -= w; 108 } while ((uio->uio_resid -= w) != 0); 109 } else if ((fp->_flags & __SLBF) == 0) { 110 /* 111 * Fully buffered: fill partially full buffer, if any, 112 * and then flush. If there is no partial buffer, write 113 * one _bf._size byte chunk directly (without copying). 114 * 115 * String output is a special case: write as many bytes 116 * as fit, but pretend we wrote everything. This makes 117 * snprintf() return the number of bytes needed, rather 118 * than the number used, and avoids its write function 119 * (so that the write function can be invalid). 120 */ 121 do { 122 GETIOV(;); 123 if ((fp->_flags & (__SALC | __SSTR)) == 124 (__SALC | __SSTR) && fp->_w < len) { 125 size_t blen = fp->_p - fp->_bf._base; 126 unsigned char *_base; 127 int _size; 128 129 /* Allocate space exponentially. */ 130 _size = fp->_bf._size; 131 do { 132 _size = (_size << 1) + 1; 133 } while (_size < blen + len); 134 _base = realloc(fp->_bf._base, 135 (size_t)(_size + 1)); 136 if (_base == NULL) 137 goto err; 138 fp->_w += _size - fp->_bf._size; 139 fp->_bf._base = _base; 140 fp->_bf._size = _size; 141 fp->_p = _base + blen; 142 } 143 w = fp->_w; 144 if (fp->_flags & __SSTR) { 145 if (len < w) 146 w = len; 147 COPY(w); /* copy MIN(fp->_w,len), */ 148 fp->_w -= w; 149 fp->_p += w; 150 w = len; /* but pretend copied all */ 151 } else if (fp->_p > fp->_bf._base && len > w) { 152 /* fill and flush */ 153 COPY(w); 154 /* fp->_w -= w; */ /* unneeded */ 155 fp->_p += w; 156 if (fflush(fp)) 157 goto err; 158 } else if (len >= (w = fp->_bf._size)) { 159 /* write directly */ 160 w = (*fp->_write)(fp->_cookie, p, w); 161 if (w <= 0) 162 goto err; 163 } else { 164 /* fill and done */ 165 w = len; 166 COPY(w); 167 fp->_w -= w; 168 fp->_p += w; 169 } 170 p += w; 171 len -= w; 172 } while ((uio->uio_resid -= w) != 0); 173 } else { 174 /* 175 * Line buffered: like fully buffered, but we 176 * must check for newlines. Compute the distance 177 * to the first newline (including the newline), 178 * or `infinity' if there is none, then pretend 179 * that the amount to write is MIN(len,nldist). 180 */ 181 nlknown = 0; 182 nldist = 0; /* XXX just to keep gcc happy */ 183 do { 184 GETIOV(nlknown = 0); 185 if (!nlknown) { 186 nl = memchr((void *)p, '\n', len); 187 nldist = nl ? nl + 1 - p : len + 1; 188 nlknown = 1; 189 } 190 s = MIN(len, nldist); 191 w = fp->_w + fp->_bf._size; 192 if (fp->_p > fp->_bf._base && s > w) { 193 COPY(w); 194 /* fp->_w -= w; */ 195 fp->_p += w; 196 if (fflush(fp)) 197 goto err; 198 } else if (s >= (w = fp->_bf._size)) { 199 w = (*fp->_write)(fp->_cookie, p, w); 200 if (w <= 0) 201 goto err; 202 } else { 203 w = s; 204 COPY(w); 205 fp->_w -= w; 206 fp->_p += w; 207 } 208 if ((nldist -= w) == 0) { 209 /* copied the newline: flush and forget */ 210 if (fflush(fp)) 211 goto err; 212 nlknown = 0; 213 } 214 p += w; 215 len -= w; 216 } while ((uio->uio_resid -= w) != 0); 217 } 218 return (0); 219 220 err: 221 fp->_flags |= __SERR; 222 return (EOF); 223 } 224