Cygwin: mmap: allow remapping part of an existing anonymous mapping
[newlib-cygwin.git] / newlib / libc / stdio / fvwrite.c
blobadd48ba99c412d909bda8db9dd0bb169c906386e
1 /*
2 * Copyright (c) 1990 The Regents of the University of California.
3 * All rights reserved.
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.
17 /* No user fns here. Pesch 15apr92. */
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"
28 #define MIN(a, b) ((a) < (b) ? (a) : (b))
29 #define COPY(n) (void) memmove ((void *) fp->_p, (void *) p, (size_t) (n))
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++; \
41 * Write some memory regions. Return zero on success, EOF on error.
43 * On systems supporting threads, this function *must* be called under
44 * _newlib_flockfile_start locking.
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.
50 int
51 __sfvwrite_r (struct _reent *ptr,
52 register FILE *fp,
53 register struct __suio *uio)
55 register size_t len;
56 register const char *p = NULL;
57 register struct __siov *iov;
58 register _READ_WRITE_RETURN_TYPE w, s;
59 char *nl;
60 int nlknown, nldist;
62 if ((len = uio->uio_resid) == 0)
63 return 0;
65 /* make sure we can write */
66 if (cantwrite (ptr, fp))
67 return EOF;
69 iov = uio->uio_iov;
70 len = 0;
72 #ifdef __SCLE
73 /* This only affects Cygwin, so calling __sputc_r *and* __swputc_r
74 * from here doesn't matter.
76 if (fp->_flags & __SCLE) /* text mode */
78 if (fp->_flags2 & __SWID)
82 GETIOV (;);
83 while (len > 0)
85 if (__swputc_r (ptr, *p, fp) == EOF)
86 return EOF;
87 p++;
88 len--;
89 uio->uio_resid--;
92 while (uio->uio_resid > 0);
94 else
98 GETIOV (;);
99 while (len > 0)
101 if (__sputc_r (ptr, *p, fp) == EOF)
102 return EOF;
103 p++;
104 len--;
105 uio->uio_resid--;
108 while (uio->uio_resid > 0);
110 return 0;
112 #endif
114 if (fp->_flags & __SNBF)
117 * Unbuffered: Split buffer in the largest multiple of BUFSIZ < INT_MAX
118 * as some legacy code may expect int instead of size_t.
122 GETIOV (;);
123 w = fp->_write (ptr, fp->_cookie, p,
124 MIN (len, INT_MAX - INT_MAX % BUFSIZ));
125 if (w <= 0)
126 goto err;
127 p += w;
128 len -= w;
130 while ((uio->uio_resid -= w) != 0);
132 else if ((fp->_flags & __SLBF) == 0)
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).
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.
149 GETIOV (;);
150 w = fp->_w;
151 if (fp->_flags & __SSTR)
153 if (len >= 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 (newsize < curpos + len + 1)
166 newsize = curpos + len + 1;
167 if (fp->_flags & __SOPT)
169 /* asnprintf leaves original buffer alone. */
170 str = (unsigned char *)_malloc_r (ptr, newsize);
171 if (!str)
173 _REENT_ERRNO(ptr) = ENOMEM;
174 goto err;
176 memcpy (str, fp->_bf._base, curpos);
177 fp->_flags = (fp->_flags & ~__SOPT) | __SMBF;
179 else
181 str = (unsigned char *)_realloc_r (ptr, fp->_bf._base,
182 newsize);
183 if (!str)
185 /* Free buffer which is no longer used and clear
186 __SMBF flag to avoid double free in fclose. */
187 _free_r (ptr, fp->_bf._base);
188 fp->_flags &= ~__SMBF;
189 /* Ensure correct errno, even if free changed it. */
190 _REENT_ERRNO(ptr) = ENOMEM;
191 goto err;
194 fp->_bf._base = str;
195 fp->_p = str + curpos;
196 fp->_bf._size = newsize;
197 w = len;
198 fp->_w = newsize - curpos;
200 if (len < 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 */
207 else if (fp->_p > fp->_bf._base || len < fp->_bf._size)
209 /* pass through the buffer */
210 w = MIN (len, w);
211 COPY (w);
212 fp->_w -= w;
213 fp->_p += w;
214 if (fp->_w == 0 && _fflush_r (ptr, fp))
215 goto err;
217 else
219 /* write directly */
220 w = ((int)MIN (len, INT_MAX)) / fp->_bf._size * fp->_bf._size;
221 w = fp->_write (ptr, fp->_cookie, p, w);
222 if (w <= 0)
223 goto err;
225 p += w;
226 len -= w;
228 while ((uio->uio_resid -= w) != 0);
230 else
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).
239 nlknown = 0;
240 nldist = 0;
243 GETIOV (nlknown = 0);
244 if (!nlknown)
246 nl = memchr ((void *) p, '\n', len);
247 nldist = nl ? nl + 1 - p : len + 1;
248 nlknown = 1;
250 s = MIN (len, nldist);
251 w = fp->_w + fp->_bf._size;
252 if (fp->_p > fp->_bf._base && s > w)
254 COPY (w);
255 /* fp->_w -= w; */
256 fp->_p += w;
257 if (_fflush_r (ptr, fp))
258 goto err;
260 else if (s >= (w = fp->_bf._size))
262 w = fp->_write (ptr, fp->_cookie, p, w);
263 if (w <= 0)
264 goto err;
266 else
268 w = s;
269 COPY (w);
270 fp->_w -= w;
271 fp->_p += w;
273 if ((nldist -= w) == 0)
275 /* copied the newline: flush and forget */
276 if (_fflush_r (ptr, fp))
277 goto err;
278 nlknown = 0;
280 p += w;
281 len -= w;
283 while ((uio->uio_resid -= w) != 0);
285 return 0;
287 err:
288 fp->_flags |= __SERR;
289 return EOF;