diff options
Diffstat (limited to 'libc/stdio/fvwrite.c')
-rw-r--r-- | libc/stdio/fvwrite.c | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/libc/stdio/fvwrite.c b/libc/stdio/fvwrite.c new file mode 100644 index 0000000..bbea672 --- /dev/null +++ b/libc/stdio/fvwrite.c @@ -0,0 +1,208 @@ +/* $OpenBSD: fvwrite.c,v 1.14 2005/08/08 08:05:36 espie Exp $ */ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include "local.h" +#include "fvwrite.h" + +/* + * Write some memory regions. Return zero on success, EOF on error. + * + * This routine is large and unsightly, but most of the ugliness due + * to the three different kinds of output buffering is handled here. + */ +int +__sfvwrite(FILE *fp, struct __suio *uio) +{ + size_t len; + char *p; + struct __siov *iov; + int w, s; + char *nl; + int nlknown, nldist; + + if ((len = uio->uio_resid) == 0) + return (0); + /* make sure we can write */ + if (cantwrite(fp)) { + errno = EBADF; + return (EOF); + } + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define COPY(n) (void)memcpy((void *)fp->_p, (void *)p, (size_t)(n)) + + iov = uio->uio_iov; + p = iov->iov_base; + len = iov->iov_len; + iov++; +#define GETIOV(extra_work) \ + while (len == 0) { \ + extra_work; \ + p = iov->iov_base; \ + len = iov->iov_len; \ + iov++; \ + } + if (fp->_flags & __SNBF) { + /* + * Unbuffered: write up to BUFSIZ bytes at a time. + */ + do { + GETIOV(;); +#if 1 /* BIONIC: don't limit to 1KB writes */ + w = (*fp->_write)(fp->_cookie, p, len); +#else + w = (*fp->_write)(fp->_cookie, p, MIN(len, BUFSIZ2)); +#endif + if (w <= 0) + goto err; + p += w; + len -= w; + } while ((uio->uio_resid -= w) != 0); + } else if ((fp->_flags & __SLBF) == 0) { + /* + * Fully buffered: fill partially full buffer, if any, + * and then flush. If there is no partial buffer, write + * one _bf._size byte chunk directly (without copying). + * + * String output is a special case: write as many bytes + * as fit, but pretend we wrote everything. This makes + * snprintf() return the number of bytes needed, rather + * than the number used, and avoids its write function + * (so that the write function can be invalid). + */ + do { + GETIOV(;); + if ((fp->_flags & (__SALC | __SSTR)) == + (__SALC | __SSTR) && fp->_w < (int)len) { + size_t blen = fp->_p - fp->_bf._base; + unsigned char *_base; + int _size; + + /* Allocate space exponentially. */ + _size = fp->_bf._size; + do { + _size = (_size << 1) + 1; + } while (_size < (int)(blen + len)); + _base = realloc(fp->_bf._base, _size + 1); + if (_base == NULL) + goto err; + fp->_w += _size - fp->_bf._size; + fp->_bf._base = _base; + fp->_bf._size = _size; + fp->_p = _base + blen; + } + w = fp->_w; + if (fp->_flags & __SSTR) { + if ((int)len < w) + w = len; + COPY(w); /* copy MIN(fp->_w,len), */ + fp->_w -= w; + fp->_p += w; + w = len; /* but pretend copied all */ + } else if (fp->_p > fp->_bf._base && (int)len > w) { + /* fill and flush */ + COPY(w); + /* fp->_w -= w; */ /* unneeded */ + fp->_p += w; + if (fflush(fp)) + goto err; + } else if ((int)len >= (w = fp->_bf._size)) { + /* write directly */ + w = (*fp->_write)(fp->_cookie, p, w); + if (w <= 0) + goto err; + } else { + /* fill and done */ + w = len; + COPY(w); + fp->_w -= w; + fp->_p += w; + } + p += w; + len -= w; + } while ((uio->uio_resid -= w) != 0); + } else { + /* + * Line buffered: like fully buffered, but we + * must check for newlines. Compute the distance + * to the first newline (including the newline), + * or `infinity' if there is none, then pretend + * that the amount to write is MIN(len,nldist). + */ + nlknown = 0; + nldist = 0; /* XXX just to keep gcc happy */ + do { + GETIOV(nlknown = 0); + if (!nlknown) { + nl = memchr((void *)p, '\n', len); + nldist = nl ? nl + 1 - p : (int)len + 1; + nlknown = 1; + } + s = MIN((int)len, nldist); + w = fp->_w + fp->_bf._size; + if (fp->_p > fp->_bf._base && s > w) { + COPY(w); + /* fp->_w -= w; */ + fp->_p += w; + if (fflush(fp)) + goto err; + } else if (s >= (w = fp->_bf._size)) { + w = (*fp->_write)(fp->_cookie, p, w); + if (w <= 0) + goto err; + } else { + w = s; + COPY(w); + fp->_w -= w; + fp->_p += w; + } + if ((nldist -= w) == 0) { + /* copied the newline: flush and forget */ + if (fflush(fp)) + goto err; + nlknown = 0; + } + p += w; + len -= w; + } while ((uio->uio_resid -= w) != 0); + } + return (0); + +err: + fp->_flags |= __SERR; + return (EOF); +} |