1 /*
2 * Copyright (c) 1990 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms are permitted
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms and that any documentation,
8 * and/or other materials related to such
9 * distribution and use acknowledge that the software was developed
10 * by the University of California, Berkeley. The name of the
11 * University may not be used to endorse or promote products derived
12 * from this software without specific prior written permission.
13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16 */
17 /* No user fns here. Pesch 15apr92. */
18
19 #define _DEFAULT_SOURCE
20 #include <stdio.h>
21 #include <string.h>
22 #include <stdlib.h>
23 #include <errno.h>
24 #include <limits.h>
25 #include "local.h"
26 #include "fvwrite.h"
27
28 #define MIN(a, b) ((a) < (b) ? (a) : (b))
29 #define COPY(n) (void) memmove ((void *) fp->_p, (void *) p, (size_t) (n))
30
31 #define GETIOV(extra_work) \
32 while (len == 0) \
33 { \
34 extra_work; \
35 p = iov->iov_base; \
36 len = iov->iov_len; \
37 iov++; \
38 }
39
40 /*
41 * Write some memory regions. Return zero on success, EOF on error.
42 *
43 * On systems supporting threads, this function *must* be called under
44 * _newlib_flockfile_start locking.
45 *
46 * This routine is large and unsightly, but most of the ugliness due
47 * to the three different kinds of output buffering is handled here.
48 */
49
50 int
_sfvwrite(register FILE * fp,register struct __suio * uio)51 _sfvwrite (
52 register FILE *fp,
53 register struct __suio *uio)
54 {
55 register size_t len;
56 register const char *p = NULL;
57 register struct __siov *iov;
58 register ssize_t w, s;
59 char *nl;
60 int nlknown, nldist;
61
62 if ((len = uio->uio_resid) == 0)
63 return 0;
64
65 /* make sure we can write */
66 if (cantwrite (ptr, fp))
67 return EOF;
68
69 iov = uio->uio_iov;
70 len = 0;
71
72 #ifdef __SCLE
73 /* This only affects Cygwin, so calling __sputc_r *and* __swputc_r
74 * from here doesn't matter.
75 */
76 if (fp->_flags & __SCLE) /* text mode */
77 {
78 if (fp->_flags2 & __SWID)
79 {
80 do
81 {
82 GETIOV (;);
83 while (len > 0)
84 {
85 if (__swputc_r (ptr, *p, fp) == EOF)
86 return EOF;
87 p++;
88 len--;
89 uio->uio_resid--;
90 }
91 }
92 while (uio->uio_resid > 0);
93 }
94 else
95 {
96 do
97 {
98 GETIOV (;);
99 while (len > 0)
100 {
101 if (__sputc_r (ptr, *p, fp) == EOF)
102 return EOF;
103 p++;
104 len--;
105 uio->uio_resid--;
106 }
107 }
108 while (uio->uio_resid > 0);
109 }
110 return 0;
111 }
112 #endif
113
114 if (fp->_flags & __SNBF)
115 {
116 /*
117 * Unbuffered: Split buffer in the largest multiple of BUFSIZ < INT_MAX
118 * as some legacy code may expect int instead of size_t.
119 */
120 do
121 {
122 GETIOV (;);
123 w = fp->_write (fp->_cookie, p,
124 MIN (len, INT_MAX - INT_MAX % BUFSIZ));
125 if (w <= 0)
126 goto err;
127 p += w;
128 len -= w;
129 }
130 while ((uio->uio_resid -= w) != 0);
131 }
132 else if ((fp->_flags & __SLBF) == 0)
133 {
134 /*
135 * Fully buffered: fill partially full buffer, if any,
136 * and then flush. If there is no partial buffer, write
137 * one _bf._size byte chunk directly (without copying).
138 *
139 * String output is a special case: write as many bytes
140 * as fit, but pretend we wrote everything. This makes
141 * snprintf() return the number of bytes needed, rather
142 * than the number used, and avoids its write function
143 * (so that the write function can be invalid). If
144 * we are dealing with the asprintf routines, we will
145 * dynamically increase the buffer size as needed.
146 */
147 do
148 {
149 GETIOV (;);
150 w = fp->_w;
151 if (fp->_flags & __SSTR)
152 {
153 if (len >= (size_t) w && fp->_flags & (__SMBF | __SOPT))
154 { /* must be asprintf family */
155 unsigned char *str;
156 int curpos = (fp->_p - fp->_bf._base);
157 /* Choose a geometric growth factor to avoid
158 quadratic realloc behavior, but use a rate less
159 than (1+sqrt(5))/2 to accomodate malloc
160 overhead. asprintf EXPECTS us to overallocate, so
161 that it can add a trailing \0 without
162 reallocating. The new allocation should thus be
163 max(prev_size*1.5, curpos+len+1). */
164 int newsize = fp->_bf._size * 3 / 2;
165 if ((size_t) newsize < curpos + len + 1)
166 newsize = curpos + len + 1;
167 if (fp->_flags & __SOPT)
168 {
169 /* asnprintf leaves original buffer alone. */
170 str = (unsigned char *)malloc (newsize);
171 if (!str)
172 {
173 _REENT_ERRNO(ptr) = ENOMEM;
174 goto err;
175 }
176 memcpy (str, fp->_bf._base, curpos);
177 fp->_flags = (fp->_flags & ~__SOPT) | __SMBF;
178 }
179 else
180 {
181 str = (unsigned char *)realloc (fp->_bf._base,
182 newsize);
183 if (!str)
184 {
185 /* Free buffer which is no longer used and clear
186 __SMBF flag to avoid double free in fclose. */
187 free (fp->_bf._base);
188 fp->_flags &= ~__SMBF;
189 /* Ensure correct errno, even if free changed it. */
190 _REENT_ERRNO(ptr) = ENOMEM;
191 goto err;
192 }
193 }
194 fp->_bf._base = str;
195 fp->_p = str + curpos;
196 fp->_bf._size = newsize;
197 w = len;
198 fp->_w = newsize - curpos;
199 }
200 if (len < (size_t) w)
201 w = len;
202 COPY (w); /* copy MIN(fp->_w,len), */
203 fp->_w -= w;
204 fp->_p += w;
205 w = len; /* but pretend copied all */
206 }
207 else if (fp->_p > fp->_bf._base || len < (size_t) fp->_bf._size)
208 {
209 /* pass through the buffer */
210 w = MIN (len, (size_t) w);
211 COPY (w);
212 fp->_w -= w;
213 fp->_p += w;
214 if (fp->_w == 0 && fflush ( fp))
215 goto err;
216 }
217 else
218 {
219 /* write directly */
220 w = ((int)MIN (len, INT_MAX)) / fp->_bf._size * fp->_bf._size;
221 w = fp->_write (fp->_cookie, p, w);
222 if (w <= 0)
223 goto err;
224 }
225 p += w;
226 len -= w;
227 }
228 while ((uio->uio_resid -= w) != 0);
229 }
230 else
231 {
232 /*
233 * Line buffered: like fully buffered, but we
234 * must check for newlines. Compute the distance
235 * to the first newline (including the newline),
236 * or `infinity' if there is none, then pretend
237 * that the amount to write is MIN(len,nldist).
238 */
239 nlknown = 0;
240 nldist = 0;
241 do
242 {
243 GETIOV (nlknown = 0);
244 if (!nlknown)
245 {
246 nl = memchr ((void *) p, '\n', len);
247 nldist = nl ? nl + 1 - p : (int) (len + 1);
248 nlknown = 1;
249 }
250 s = MIN (len, (size_t) nldist);
251 w = fp->_w + fp->_bf._size;
252 if (fp->_p > fp->_bf._base && s > w)
253 {
254 COPY (w);
255 /* fp->_w -= w; */
256 fp->_p += w;
257 if (fflush ( fp))
258 goto err;
259 }
260 else if (s >= (w = fp->_bf._size))
261 {
262 w = fp->_write (fp->_cookie, p, w);
263 if (w <= 0)
264 goto err;
265 }
266 else
267 {
268 w = s;
269 COPY (w);
270 fp->_w -= w;
271 fp->_p += w;
272 }
273 if ((nldist -= w) == 0)
274 {
275 /* copied the newline: flush and forget */
276 if (fflush ( fp))
277 goto err;
278 nlknown = 0;
279 }
280 p += w;
281 len -= w;
282 }
283 while ((uio->uio_resid -= w) != 0);
284 }
285 return 0;
286
287 err:
288 fp->_flags |= __SERR;
289 return EOF;
290 }
291