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