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 <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  * On systems supporting threads, this function *must* be called under
44  * _newlib_flockfile_start locking.
45  *
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.
48  */
49 
50 int
_sfvwrite(register FILE * fp,register struct __suio * uio)51 _sfvwrite (
52        register FILE *fp,
53        register struct __suio *uio)
54 {
55   register size_t len;
56   register const char *p = NULL;
57   register struct __siov *iov;
58   register ssize_t w, s;
59   char *nl;
60   int nlknown, nldist;
61 
62   if ((len = uio->uio_resid) == 0)
63     return 0;
64 
65   /* make sure we can write */
66   if (cantwrite (ptr, fp))
67     return EOF;
68 
69   iov = uio->uio_iov;
70   len = 0;
71 
72 #ifdef __SCLE
73   /* This only affects Cygwin, so calling __sputc_r *and* __swputc_r
74    * from here doesn't matter.
75    */
76   if (fp->_flags & __SCLE) /* text mode */
77     {
78       if (fp->_flags2 & __SWID)
79 	{
80 	  do
81 	    {
82 	      GETIOV (;);
83 	      while (len > 0)
84 		{
85 		  if (__swputc_r (ptr, *p, fp) == EOF)
86 		    return EOF;
87 		  p++;
88 		  len--;
89 		  uio->uio_resid--;
90 		}
91 	    }
92 	  while (uio->uio_resid > 0);
93 	}
94       else
95 	{
96 	  do
97 	    {
98 	      GETIOV (;);
99 	      while (len > 0)
100 		{
101 		  if (__sputc_r (ptr, *p, fp) == EOF)
102 		    return EOF;
103 		  p++;
104 		  len--;
105 		  uio->uio_resid--;
106 		}
107 	    }
108 	  while (uio->uio_resid > 0);
109 	}
110       return 0;
111     }
112 #endif
113 
114   if (fp->_flags & __SNBF)
115     {
116       /*
117        * Unbuffered: Split buffer in the largest multiple of BUFSIZ < INT_MAX
118        * as some legacy code may expect int instead of size_t.
119        */
120       do
121 	{
122 	  GETIOV (;);
123 	  w = fp->_write (fp->_cookie, p,
124 			  MIN (len, INT_MAX - INT_MAX % BUFSIZ));
125 	  if (w <= 0)
126 	    goto err;
127 	  p += w;
128 	  len -= w;
129 	}
130       while ((uio->uio_resid -= w) != 0);
131     }
132   else if ((fp->_flags & __SLBF) == 0)
133     {
134       /*
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).
138        *
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.
146        */
147       do
148 	{
149 	  GETIOV (;);
150 	  w = fp->_w;
151 	  if (fp->_flags & __SSTR)
152 	    {
153               if (len >= (size_t) 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 ((size_t) newsize < curpos + len + 1)
166 		    newsize = curpos + len + 1;
167 		  if (fp->_flags & __SOPT)
168 		    {
169 		      /* asnprintf leaves original buffer alone.  */
170 		      str = (unsigned char *)malloc (newsize);
171 		      if (!str)
172 			{
173 			  _REENT_ERRNO(ptr) = ENOMEM;
174 			  goto err;
175 			}
176 		      memcpy (str, fp->_bf._base, curpos);
177 		      fp->_flags = (fp->_flags & ~__SOPT) | __SMBF;
178 		    }
179 		  else
180 		    {
181 		      str = (unsigned char *)realloc (fp->_bf._base,
182 							 newsize);
183 		      if (!str)
184 			{
185 			  /* Free buffer which is no longer used and clear
186 			     __SMBF flag to avoid double free in fclose.  */
187 			  free (fp->_bf._base);
188 			  fp->_flags &=  ~__SMBF;
189 			  /* Ensure correct errno, even if free changed it.  */
190 			  _REENT_ERRNO(ptr) = ENOMEM;
191 			  goto err;
192 			}
193 		    }
194 		  fp->_bf._base = str;
195 		  fp->_p = str + curpos;
196 		  fp->_bf._size = newsize;
197 		  w = len;
198 		  fp->_w = newsize - curpos;
199 		}
200 	      if (len < (size_t) 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 */
206 	    }
207 	  else if (fp->_p > fp->_bf._base || len < (size_t) fp->_bf._size)
208 	    {
209 	      /* pass through the buffer */
210 	      w = MIN (len, (size_t) w);
211 	      COPY (w);
212 	      fp->_w -= w;
213 	      fp->_p += w;
214 	      if (fp->_w == 0 && fflush ( fp))
215 		goto err;
216 	    }
217 	  else
218 	    {
219 	      /* write directly */
220 	      w = ((int)MIN (len, INT_MAX)) / fp->_bf._size * fp->_bf._size;
221 	      w = fp->_write (fp->_cookie, p, w);
222 	      if (w <= 0)
223 		goto err;
224 	    }
225 	  p += w;
226 	  len -= w;
227 	}
228       while ((uio->uio_resid -= w) != 0);
229     }
230   else
231     {
232       /*
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).
238        */
239       nlknown = 0;
240       nldist = 0;
241       do
242 	{
243 	  GETIOV (nlknown = 0);
244 	  if (!nlknown)
245 	    {
246 	      nl = memchr ((void *) p, '\n', len);
247 	      nldist = nl ? nl + 1 - p : (int) (len + 1);
248 	      nlknown = 1;
249 	    }
250 	  s = MIN (len, (size_t) nldist);
251 	  w = fp->_w + fp->_bf._size;
252 	  if (fp->_p > fp->_bf._base && s > w)
253 	    {
254 	      COPY (w);
255 	      /* fp->_w -= w; */
256 	      fp->_p += w;
257 	      if (fflush ( fp))
258 		goto err;
259 	    }
260 	  else if (s >= (w = fp->_bf._size))
261 	    {
262 	      w = fp->_write (fp->_cookie, p, w);
263 	      if (w <= 0)
264 		goto err;
265 	    }
266 	  else
267 	    {
268 	      w = s;
269 	      COPY (w);
270 	      fp->_w -= w;
271 	      fp->_p += w;
272 	    }
273 	  if ((nldist -= w) == 0)
274 	    {
275 	      /* copied the newline: flush and forget */
276 	      if (fflush ( fp))
277 		goto err;
278 	      nlknown = 0;
279 	    }
280 	  p += w;
281 	  len -= w;
282 	}
283       while ((uio->uio_resid -= w) != 0);
284     }
285   return 0;
286 
287 err:
288   fp->_flags |= __SERR;
289   return EOF;
290 }
291