OSDN Git Service

2007-03-15 Eric Blake <ebb9@byu.net>
[pf3gnuchains/pf3gnuchains3x.git] / newlib / libc / stdio / fvwrite.c
1 /*
2  * Copyright (c) 1990, 2006, 2007 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 "local.h"
25 #include "fvwrite.h"
26
27 #define MIN(a, b) ((a) < (b) ? (a) : (b))
28 #define COPY(n)   _CAST_VOID memmove ((_PTR) fp->_p, (_PTR) p, (size_t) (n))
29
30 #define GETIOV(extra_work) \
31   while (len == 0) \
32     { \
33       extra_work; \
34       p = iov->iov_base; \
35       len = iov->iov_len; \
36       iov++; \
37     }
38
39 /*
40  * Write some memory regions.  Return zero on success, EOF on error.
41  *
42  * This routine is large and unsightly, but most of the ugliness due
43  * to the three different kinds of output buffering is handled here.
44  */
45
46 int
47 _DEFUN(__sfvwrite_r, (ptr, fp, uio),
48        struct _reent *ptr _AND
49        register FILE *fp _AND
50        register struct __suio *uio)
51 {
52   register size_t len;
53   register _CONST char *p = NULL;
54   register struct __siov *iov;
55   register int 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     {
65       fp->_flags |= __SERR;
66       ptr->_errno = EBADF;
67       return EOF;
68     }
69
70   iov = uio->uio_iov;
71   len = 0;
72
73 #ifdef __SCLE
74   if (fp->_flags & __SCLE) /* text mode */
75     {
76       do
77         {
78           GETIOV (;);
79           while (len > 0)
80             {
81               if (putc (*p, fp) == EOF)
82                 return EOF;
83               p++;
84               len--;
85               uio->uio_resid--;
86             }
87         }
88       while (uio->uio_resid > 0);
89       return 0;
90     }
91 #endif
92
93   if (fp->_flags & __SNBF)
94     {
95       /*
96        * Unbuffered: write up to BUFSIZ bytes at a time.
97        */
98       do
99         {
100           GETIOV (;);
101           w = (*fp->_write) (fp->_cookie, p, MIN (len, BUFSIZ));
102           if (w <= 0)
103             goto err;
104           p += w;
105           len -= w;
106         }
107       while ((uio->uio_resid -= w) != 0);
108     }
109   else if ((fp->_flags & __SLBF) == 0)
110     {
111       /*
112        * Fully buffered: fill partially full buffer, if any,
113        * and then flush.  If there is no partial buffer, write
114        * one _bf._size byte chunk directly (without copying).
115        *
116        * String output is a special case: write as many bytes
117        * as fit, but pretend we wrote everything.  This makes
118        * snprintf() return the number of bytes needed, rather
119        * than the number used, and avoids its write function
120        * (so that the write function can be invalid).  If
121        * we are dealing with the asprintf routines, we will
122        * dynamically increase the buffer size as needed.
123        */
124       do
125         {
126           GETIOV (;);
127           w = fp->_w;
128           if (fp->_flags & __SSTR)
129             {
130               if (len >= w && fp->_flags & __SMBF)
131                 { /* must be asprintf family */
132                   unsigned char *str;
133                   int curpos = (fp->_p - fp->_bf._base);
134                   /* Choose a geometric growth factor to avoid
135                      quadratic realloc behavior, but use a rate less
136                      than (1+sqrt(5))/2 to accomodate malloc
137                      overhead. asprintf EXPECTS us to overallocate, so
138                      that it can add a trailing \0 without
139                      reallocating.  The new allocation should thus be
140                      max(prev_size*1.5, curpos+len+1). */
141                   int newsize = fp->_bf._size * 3 / 2;
142                   if (newsize < curpos + len + 1)
143                     newsize = curpos + len + 1;
144                   str = (unsigned char *)_realloc_r (ptr, fp->_bf._base,
145                                                      newsize);
146                   if (!str)
147                     {
148                       /* Free buffer which is no longer used.  */
149                       _free_r (ptr, fp->_bf._base);
150                       /* Ensure correct errno, even if free changed it.  */
151                       ptr->_errno = ENOMEM;
152                       goto err;
153                     }
154                   fp->_bf._base = str;
155                   fp->_p = str + curpos;
156                   fp->_bf._size = newsize;
157                   w = len;
158                   fp->_w = newsize - curpos;
159                 }
160               if (len < w)
161                 w = len;
162               COPY (w);         /* copy MIN(fp->_w,len), */
163               fp->_w -= w;
164               fp->_p += w;
165               w = len;          /* but pretend copied all */
166             }
167           else if (fp->_p > fp->_bf._base && len > w)
168             {
169               /* fill and flush */
170               COPY (w);
171               /* fp->_w -= w; *//* unneeded */
172               fp->_p += w;
173               if (fflush (fp))
174                 goto err;
175             }
176           else if (len >= (w = fp->_bf._size))
177             {
178               /* write directly */
179               w = (*fp->_write) (fp->_cookie, p, w);
180               if (w <= 0)
181                 goto err;
182             }
183           else
184             {
185               /* fill and done */
186               w = len;
187               COPY (w);
188               fp->_w -= w;
189               fp->_p += w;
190             }
191           p += w;
192           len -= w;
193         }
194       while ((uio->uio_resid -= w) != 0);
195     }
196   else
197     {
198       /*
199        * Line buffered: like fully buffered, but we
200        * must check for newlines.  Compute the distance
201        * to the first newline (including the newline),
202        * or `infinity' if there is none, then pretend
203        * that the amount to write is MIN(len,nldist).
204        */
205       nlknown = 0;
206       nldist = 0;
207       do
208         {
209           GETIOV (nlknown = 0);
210           if (!nlknown)
211             {
212               nl = memchr ((_PTR) p, '\n', len);
213               nldist = nl ? nl + 1 - p : len + 1;
214               nlknown = 1;
215             }
216           s = MIN (len, nldist);
217           w = fp->_w + fp->_bf._size;
218           if (fp->_p > fp->_bf._base && s > w)
219             {
220               COPY (w);
221               /* fp->_w -= w; */
222               fp->_p += w;
223               if (fflush (fp))
224                 goto err;
225             }
226           else if (s >= (w = fp->_bf._size))
227             {
228               w = (*fp->_write) (fp->_cookie, p, w);
229               if (w <= 0)
230                 goto err;
231             }
232           else
233             {
234               w = s;
235               COPY (w);
236               fp->_w -= w;
237               fp->_p += w;
238             }
239           if ((nldist -= w) == 0)
240             {
241               /* copied the newline: flush and forget */
242               if (fflush (fp))
243                 goto err;
244               nlknown = 0;
245             }
246           p += w;
247           len -= w;
248         }
249       while ((uio->uio_resid -= w) != 0);
250     }
251   return 0;
252
253 err:
254   fp->_flags |= __SERR;
255   return EOF;
256 }