xref: /original-bsd/lib/libc/stdio/fvwrite.c (revision c3e32dec)
1 /*-
2  * Copyright (c) 1990, 1993
3  *	The Regents of the University of California.  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	8.1 (Berkeley) 06/04/93";
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