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