source: trunk/libs/newlib/src/newlib/libc/stdio/fvwrite.c @ 567

Last change on this file since 567 was 444, checked in by satin@…, 6 years ago

add newlib,libalmos-mkh, restructure shared_syscalls.h and mini-libc

File size: 6.7 KB
Line 
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
47int
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
262err:
263  fp->_flags |= __SERR;
264  return EOF;
265}
Note: See TracBrowser for help on using the repository browser.