11573Srgrimes/*-
21573Srgrimes * Copyright (c) 1990, 1993
31573Srgrimes *	The Regents of the University of California.  All rights reserved.
41573Srgrimes *
51573Srgrimes * This code is derived from software contributed to Berkeley by
61573Srgrimes * Chris Torek.
71573Srgrimes *
81573Srgrimes * Redistribution and use in source and binary forms, with or without
91573Srgrimes * modification, are permitted provided that the following conditions
101573Srgrimes * are met:
111573Srgrimes * 1. Redistributions of source code must retain the above copyright
121573Srgrimes *    notice, this list of conditions and the following disclaimer.
131573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141573Srgrimes *    notice, this list of conditions and the following disclaimer in the
151573Srgrimes *    documentation and/or other materials provided with the distribution.
16249808Semaste * 3. Neither the name of the University nor the names of its contributors
171573Srgrimes *    may be used to endorse or promote products derived from this software
181573Srgrimes *    without specific prior written permission.
191573Srgrimes *
201573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231573Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301573Srgrimes * SUCH DAMAGE.
311573Srgrimes */
321573Srgrimes
331573Srgrimes#if defined(LIBC_SCCS) && !defined(lint)
341573Srgrimesstatic char sccsid[] = "@(#)fvwrite.c	8.1 (Berkeley) 6/4/93";
351573Srgrimes#endif /* LIBC_SCCS and not lint */
3692986Sobrien#include <sys/cdefs.h>
3792986Sobrien__FBSDID("$FreeBSD$");
381573Srgrimes
391573Srgrimes#include <stdio.h>
4037487Speter#include <stdlib.h>
411573Srgrimes#include <string.h>
421573Srgrimes#include "local.h"
431573Srgrimes#include "fvwrite.h"
441573Srgrimes
451573Srgrimes/*
461573Srgrimes * Write some memory regions.  Return zero on success, EOF on error.
471573Srgrimes *
481573Srgrimes * This routine is large and unsightly, but most of the ugliness due
491573Srgrimes * to the three different kinds of output buffering is handled here.
501573Srgrimes */
5116586Sjraynardint
52249810Semaste__sfvwrite(FILE *fp, struct __suio *uio)
531573Srgrimes{
5492889Sobrien	size_t len;
5592889Sobrien	char *p;
5692889Sobrien	struct __siov *iov;
5792889Sobrien	int w, s;
581573Srgrimes	char *nl;
5982838Sache	int nlknown, nldist;
601573Srgrimes
61199781Swollman	if (uio->uio_resid == 0)
621573Srgrimes		return (0);
631573Srgrimes	/* make sure we can write */
64130232Sdas	if (prepwrite(fp) != 0)
651573Srgrimes		return (EOF);
661573Srgrimes
671573Srgrimes#define	MIN(a, b) ((a) < (b) ? (a) : (b))
681573Srgrimes#define	COPY(n)	  (void)memcpy((void *)fp->_p, (void *)p, (size_t)(n))
691573Srgrimes
701573Srgrimes	iov = uio->uio_iov;
711573Srgrimes	p = iov->iov_base;
721573Srgrimes	len = iov->iov_len;
731573Srgrimes	iov++;
741573Srgrimes#define GETIOV(extra_work) \
751573Srgrimes	while (len == 0) { \
761573Srgrimes		extra_work; \
771573Srgrimes		p = iov->iov_base; \
781573Srgrimes		len = iov->iov_len; \
791573Srgrimes		iov++; \
801573Srgrimes	}
811573Srgrimes	if (fp->_flags & __SNBF) {
821573Srgrimes		/*
831573Srgrimes		 * Unbuffered: write up to BUFSIZ bytes at a time.
841573Srgrimes		 */
851573Srgrimes		do {
861573Srgrimes			GETIOV(;);
8782838Sache			w = _swrite(fp, p, MIN(len, BUFSIZ));
881573Srgrimes			if (w <= 0)
891573Srgrimes				goto err;
901573Srgrimes			p += w;
911573Srgrimes			len -= w;
921573Srgrimes		} while ((uio->uio_resid -= w) != 0);
931573Srgrimes	} else if ((fp->_flags & __SLBF) == 0) {
941573Srgrimes		/*
951573Srgrimes		 * Fully buffered: fill partially full buffer, if any,
961573Srgrimes		 * and then flush.  If there is no partial buffer, write
971573Srgrimes		 * one _bf._size byte chunk directly (without copying).
981573Srgrimes		 *
991573Srgrimes		 * String output is a special case: write as many bytes
10031981Sache		 * as fit, but pretend we wrote everything.  This makes
10131981Sache		 * snprintf() return the number of bytes needed, rather
10231981Sache		 * than the number used, and avoids its write function
10331981Sache		 * (so that the write function can be invalid).
1041573Srgrimes		 */
1051573Srgrimes		do {
1061573Srgrimes			GETIOV(;);
10737487Speter			if ((fp->_flags & (__SALC | __SSTR)) ==
10837487Speter			    (__SALC | __SSTR) && fp->_w < len) {
10937487Speter				size_t blen = fp->_p - fp->_bf._base;
11037487Speter
11137487Speter				/*
11237487Speter				 * Alloc an extra 128 bytes (+ 1 for NULL)
11337487Speter				 * so we don't call realloc(3) so often.
11437487Speter				 */
11537487Speter				fp->_w = len + 128;
11637487Speter				fp->_bf._size = blen + len + 128;
11737487Speter				fp->_bf._base =
11839327Simp				    reallocf(fp->_bf._base, fp->_bf._size + 1);
11937487Speter				if (fp->_bf._base == NULL)
12037487Speter					goto err;
12137487Speter				fp->_p = fp->_bf._base + blen;
12237487Speter			}
1231573Srgrimes			w = fp->_w;
1241573Srgrimes			if (fp->_flags & __SSTR) {
1251573Srgrimes				if (len < w)
1261573Srgrimes					w = len;
12731981Sache				if (w > 0) {
12831981Sache					COPY(w);        /* copy MIN(fp->_w,len), */
12931981Sache					fp->_w -= w;
13031981Sache					fp->_p += w;
13131981Sache				}
1321573Srgrimes				w = len;	/* but pretend copied all */
1331573Srgrimes			} else if (fp->_p > fp->_bf._base && len > w) {
1341573Srgrimes				/* fill and flush */
1351573Srgrimes				COPY(w);
1361573Srgrimes				/* fp->_w -= w; */ /* unneeded */
1371573Srgrimes				fp->_p += w;
13871579Sdeischen				if (__fflush(fp))
1391573Srgrimes					goto err;
1401573Srgrimes			} else if (len >= (w = fp->_bf._size)) {
1411573Srgrimes				/* write directly */
14282838Sache				w = _swrite(fp, p, w);
1431573Srgrimes				if (w <= 0)
1441573Srgrimes					goto err;
1451573Srgrimes			} else {
1461573Srgrimes				/* fill and done */
1471573Srgrimes				w = len;
1481573Srgrimes				COPY(w);
1491573Srgrimes				fp->_w -= w;
1501573Srgrimes				fp->_p += w;
1511573Srgrimes			}
1521573Srgrimes			p += w;
1531573Srgrimes			len -= w;
1541573Srgrimes		} while ((uio->uio_resid -= w) != 0);
1551573Srgrimes	} else {
1561573Srgrimes		/*
1571573Srgrimes		 * Line buffered: like fully buffered, but we
1581573Srgrimes		 * must check for newlines.  Compute the distance
1591573Srgrimes		 * to the first newline (including the newline),
1601573Srgrimes		 * or `infinity' if there is none, then pretend
1611573Srgrimes		 * that the amount to write is MIN(len,nldist).
1621573Srgrimes		 */
1631573Srgrimes		nlknown = 0;
1641573Srgrimes		nldist = 0;	/* XXX just to keep gcc happy */
1651573Srgrimes		do {
1661573Srgrimes			GETIOV(nlknown = 0);
1671573Srgrimes			if (!nlknown) {
1681573Srgrimes				nl = memchr((void *)p, '\n', len);
1691573Srgrimes				nldist = nl ? nl + 1 - p : len + 1;
1701573Srgrimes				nlknown = 1;
1711573Srgrimes			}
1721573Srgrimes			s = MIN(len, nldist);
1731573Srgrimes			w = fp->_w + fp->_bf._size;
1741573Srgrimes			if (fp->_p > fp->_bf._base && s > w) {
1751573Srgrimes				COPY(w);
1761573Srgrimes				/* fp->_w -= w; */
1771573Srgrimes				fp->_p += w;
17871579Sdeischen				if (__fflush(fp))
1791573Srgrimes					goto err;
1801573Srgrimes			} else if (s >= (w = fp->_bf._size)) {
18182838Sache				w = _swrite(fp, p, w);
1821573Srgrimes				if (w <= 0)
1831573Srgrimes				 	goto err;
1841573Srgrimes			} else {
1851573Srgrimes				w = s;
1861573Srgrimes				COPY(w);
1871573Srgrimes				fp->_w -= w;
1881573Srgrimes				fp->_p += w;
1891573Srgrimes			}
1901573Srgrimes			if ((nldist -= w) == 0) {
1911573Srgrimes				/* copied the newline: flush and forget */
19271579Sdeischen				if (__fflush(fp))
1931573Srgrimes					goto err;
1941573Srgrimes				nlknown = 0;
1951573Srgrimes			}
1961573Srgrimes			p += w;
1971573Srgrimes			len -= w;
1981573Srgrimes		} while ((uio->uio_resid -= w) != 0);
1991573Srgrimes	}
2001573Srgrimes	return (0);
2011573Srgrimes
2021573Srgrimeserr:
2031573Srgrimes	fp->_flags |= __SERR;
2041573Srgrimes	return (EOF);
2051573Srgrimes}
206