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