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